mastodon-ios/MastodonSDK/Sources/MastodonUI/View/Content/NotificationView+ViewModel....

210 lines
8.4 KiB
Swift

//
// NotificationView+ViewModel.swift
//
//
// Created by MainasuK on 2022-1-21.
//
import os.log
import UIKit
import Combine
import Meta
import MastodonSDK
import MastodonAsset
import MastodonLocalization
import MastodonExtension
import CoreData
import CoreDataStack
extension NotificationView {
public final class ViewModel: ObservableObject {
public var disposeBag = Set<AnyCancellable>()
public var objects = Set<NSManagedObject>()
let logger = Logger(subsystem: "NotificationView", category: "ViewModel")
@Published public var userIdentifier: UserIdentifier? // me
@Published public var notificationIndicatorText: MetaContent?
@Published public var authorAvatarImage: UIImage?
@Published public var authorAvatarImageURL: URL?
@Published public var authorName: MetaContent?
@Published public var authorUsername: String?
@Published public var isMyself = false
@Published public var isMuting = false
@Published public var isBlocking = false
@Published public var timestamp: Date?
@Published public var followRequestState = MastodonFollowRequestState(state: .none)
@Published public var transientFollowRequestState = MastodonFollowRequestState(state: .none)
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.share()
.eraseToAnyPublisher()
}
}
extension NotificationView.ViewModel {
func bind(notificationView: NotificationView) {
bindAuthor(notificationView: notificationView)
bindAuthorMenu(notificationView: notificationView)
bindFollowRequest(notificationView: notificationView)
$userIdentifier
.assign(to: \.userIdentifier, on: notificationView.statusView.viewModel)
.store(in: &disposeBag)
$userIdentifier
.assign(to: \.userIdentifier, on: notificationView.quoteStatusView.viewModel)
.store(in: &disposeBag)
}
private func bindAuthor(notificationView: NotificationView) {
// avatar
Publishers.CombineLatest(
$authorAvatarImage,
$authorAvatarImageURL
)
.sink { image, url in
let configuration: AvatarImageView.Configuration = {
if let image = image {
return AvatarImageView.Configuration(image: image)
} else {
return AvatarImageView.Configuration(url: url)
}
}()
notificationView.avatarButton.avatarImageView.configure(configuration: configuration)
notificationView.avatarButton.avatarImageView.configure(cornerConfiguration: .init(corner: .fixed(radius: 12)))
}
.store(in: &disposeBag)
// name
$authorName
.sink { metaContent in
let metaContent = metaContent ?? PlaintextMetaContent(string: " ")
notificationView.authorNameLabel.configure(content: metaContent)
}
.store(in: &disposeBag)
// username
$authorUsername
.map { text -> String in
guard let text = text else { return "" }
return "@\(text)"
}
.sink { username in
let metaContent = PlaintextMetaContent(string: username)
notificationView.authorUsernameLabel.configure(content: metaContent)
}
.store(in: &disposeBag)
// timestamp
Publishers.CombineLatest(
$timestamp,
timestampUpdatePublisher.prepend(Date()).eraseToAnyPublisher()
)
.sink { [weak self] timestamp, _ in
guard let self = self else { return }
guard let timestamp = timestamp else {
notificationView.dateLabel.configure(content: PlaintextMetaContent(string: ""))
return
}
let text = timestamp.localizedTimeAgoSinceNow
notificationView.dateLabel.configure(content: PlaintextMetaContent(string: text))
}
.store(in: &disposeBag)
// notification type indicator
$notificationIndicatorText
.sink { text in
if let text = text {
notificationView.notificationTypeIndicatorLabel.configure(content: text)
} else {
notificationView.notificationTypeIndicatorLabel.reset()
}
}
.store(in: &disposeBag)
}
private func bindAuthorMenu(notificationView: NotificationView) {
Publishers.CombineLatest4(
$authorName,
$isMuting,
$isBlocking,
$isMyself
)
.sink { authorName, isMuting, isBlocking, isMyself in
guard let name = authorName?.string else {
notificationView.menuButton.menu = nil
return
}
let menuContext = NotificationView.AuthorMenuContext(
name: name,
isMuting: isMuting,
isBlocking: isBlocking,
isMyself: isMyself
)
notificationView.menuButton.menu = notificationView.setupAuthorMenu(menuContext: menuContext)
notificationView.menuButton.showsMenuAsPrimaryAction = true
notificationView.menuButton.isHidden = menuContext.isMyself
}
.store(in: &disposeBag)
}
private func bindFollowRequest(notificationView: NotificationView) {
Publishers.CombineLatest(
$followRequestState,
$transientFollowRequestState
)
.sink { followRequestState, transientFollowRequestState in
switch followRequestState.state {
case .isAccept:
notificationView.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
notificationView.acceptFollowRequestButton.isUserInteractionEnabled = false
notificationView.acceptFollowRequestButton.setImage(nil, for: .normal)
notificationView.acceptFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.accepted, for: .normal)
case .isReject:
notificationView.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
notificationView.rejectFollowRequestButton.isUserInteractionEnabled = false
notificationView.rejectFollowRequestButton.setImage(nil, for: .normal)
notificationView.rejectFollowRequestButton.setTitle(L10n.Scene.Notification.FollowRequest.rejected, for: .normal)
default:
break
}
let state = transientFollowRequestState.state
if state == .isAccepting {
notificationView.acceptFollowRequestActivityIndicatorView.startAnimating()
notificationView.acceptFollowRequestButton.tintColor = .clear
notificationView.acceptFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.acceptFollowRequestActivityIndicatorView.stopAnimating()
notificationView.acceptFollowRequestButton.tintColor = .white
notificationView.acceptFollowRequestButton.setTitleColor(.white, for: .normal)
}
if state == .isRejecting {
notificationView.rejectFollowRequestActivityIndicatorView.startAnimating()
notificationView.rejectFollowRequestButton.tintColor = .clear
notificationView.rejectFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.rejectFollowRequestActivityIndicatorView.stopAnimating()
notificationView.rejectFollowRequestButton.tintColor = .black
notificationView.rejectFollowRequestButton.setTitleColor(.black, for: .normal)
}
UIView.animate(withDuration: 0.3) {
if state == .isAccept {
notificationView.rejectFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
if state == .isReject {
notificationView.acceptFollowRequestButtonShadowBackgroundContainer.isHidden = true
}
}
}
.store(in: &disposeBag)
}
}