From 018cf540845f0373c7eab3833f2b90a5e6e31aae Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Sat, 20 Jan 2024 20:03:45 +0100 Subject: [PATCH] Don't use publisher for most properties in NotificationView (IOS-192) Timestamp and A11y are still missing (and A11y is broken atm) --- .../Notification/NotificationSection.swift | 1 - .../NotificationView+Configuration.swift | 74 ++++++++++------ .../NotificationView+ViewModel.swift | 84 +------------------ .../NotificationView/NotificationView.swift | 2 +- 4 files changed, 52 insertions(+), 109 deletions(-) diff --git a/Mastodon/Diffable/Notification/NotificationSection.swift b/Mastodon/Diffable/Notification/NotificationSection.swift index 0b446336f..e95c681db 100644 --- a/Mastodon/Diffable/Notification/NotificationSection.swift +++ b/Mastodon/Diffable/Notification/NotificationSection.swift @@ -73,7 +73,6 @@ extension NotificationSection { viewModel: NotificationTableViewCell.ViewModel, configuration: Configuration ) { - cell.notificationView.viewModel.context = context cell.notificationView.viewModel.authContext = configuration.authContext StatusSection.setupStatusPollDataSource( diff --git a/Mastodon/Scene/Notification/NotificationView/NotificationView+Configuration.swift b/Mastodon/Scene/Notification/NotificationView/NotificationView+Configuration.swift index 1f77a2d33..0cb9da883 100644 --- a/Mastodon/Scene/Notification/NotificationView/NotificationView+Configuration.swift +++ b/Mastodon/Scene/Notification/NotificationView/NotificationView+Configuration.swift @@ -64,23 +64,32 @@ extension NotificationView { let author = notification.account // author avatar - viewModel.authorAvatarImageURL = author.avatarImageURL() + let configuration = AvatarImageView.Configuration(url: author.avatarImageURL()) + avatarButton.avatarImageView.configure(configuration: configuration) + avatarButton.avatarImageView.configure(cornerConfiguration: .init(corner: .fixed(radius: 12))) // author name + let metaAuthorName: MetaContent do { let content = MastodonContent(content: author.displayNameWithFallback, emojis: author.emojis.asDictionary) - viewModel.authorName = try MastodonMetaContent.convert(document: content) + metaAuthorName = try MastodonMetaContent.convert(document: content) } catch { assertionFailure(error.localizedDescription) - viewModel.authorName = PlaintextMetaContent(string: author.displayNameWithFallback) + metaAuthorName = PlaintextMetaContent(string: author.displayNameWithFallback) } + authorNameLabel.configure(content: metaAuthorName) + + // username + let metaUsername = PlaintextMetaContent(string: "@\(author.acct)") + authorUsernameLabel.configure(content: metaUsername) - viewModel.authorUsername = author.acct viewModel.timestamp = notification.entity.createdAt - viewModel.visibility = notification.entity.status?.mastodonVisibility ?? ._other("") + let visibility = notification.entity.status?.mastodonVisibility ?? ._other("") + visibilityIconImageView.image = visibility.image // notification type indicator + let notificationIndicatorText: MetaContent? if let type = MastodonNotificationType(rawValue: notification.entity.type.rawValue) { self.viewModel.type = type @@ -92,64 +101,81 @@ extension NotificationView { } return metaContent } - + switch type { case .follow: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.followedYou, emojis: author.emojis.asDictionary ) case .followRequest: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.requestToFollowYou, emojis: author.emojis.asDictionary ) case .mention: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.mentionedYou, emojis: author.emojis.asDictionary ) case .reblog: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.rebloggedYourPost, emojis: author.emojis.asDictionary ) case .favourite: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.favoritedYourPost, emojis: author.emojis.asDictionary ) case .poll: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: L10n.Scene.Notification.NotificationDescription.pollHasEnded, emojis: author.emojis.asDictionary ) case .status: - self.viewModel.notificationIndicatorText = createMetaContent( + notificationIndicatorText = createMetaContent( text: .empty, emojis: author.emojis.asDictionary ) case ._other: - self.viewModel.notificationIndicatorText = nil + notificationIndicatorText = nil } } else { - self.viewModel.notificationIndicatorText = nil + notificationIndicatorText = nil } - + + if let notificationIndicatorText { + notificationTypeIndicatorLabel.configure(content: notificationIndicatorText) + } else { + notificationTypeIndicatorLabel.reset() + } + if let me = viewModel.authContext?.mastodonAuthenticationBox.authentication.account() { - viewModel.isMyself = (author == me) - + let isMyself = (author == me) + let isMuting: Bool + let isBlocking: Bool + if let relationship = notification.relationship { - viewModel.isMuting = relationship.muting - viewModel.isBlocking = relationship.blocking || relationship.domainBlocking - viewModel.isFollowed = relationship.following + isMuting = relationship.muting + isBlocking = relationship.blocking || relationship.domainBlocking } else { - viewModel.isMuting = false - viewModel.isBlocking = false - viewModel.isFollowed = false + isMuting = false + isBlocking = false } + + let menuContext = NotificationView.AuthorMenuContext(name: metaAuthorName.string, isMuting: isMuting, isBlocking: isBlocking, isMyself: isMyself) + let (menu, actions) = setupAuthorMenu(menuContext: menuContext) + menuButton.menu = menu + authorActions = actions + menuButton.showsMenuAsPrimaryAction = true + + menuButton.isHidden = menuContext.isMyself + } + + viewModel.followRequestState = notification.followRequestState viewModel.transientFollowRequestState = notification.transientFollowRequestState } diff --git a/Mastodon/Scene/Notification/NotificationView/NotificationView+ViewModel.swift b/Mastodon/Scene/Notification/NotificationView/NotificationView+ViewModel.swift index 6bec29e20..0117395a5 100644 --- a/Mastodon/Scene/Notification/NotificationView/NotificationView+ViewModel.swift +++ b/Mastodon/Scene/Notification/NotificationView/NotificationView+ViewModel.swift @@ -21,25 +21,15 @@ extension NotificationView { public final class ViewModel: ObservableObject { public var disposeBag = Set() - @Published public var context: AppContext? @Published public var authContext: AuthContext? @Published public var type: MastodonNotificationType? @Published public var notificationIndicatorText: MetaContent? - @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 isTranslated = false - @Published public var isFollowed = false - + @Published public var timestamp: Date? - - @Published public var visibility: MastodonVisibility = .public @Published public var followRequestState = MastodonFollowRequestState(state: .none) @Published public var transientFollowRequestState = MastodonFollowRequestState(state: .none) @@ -54,12 +44,8 @@ extension NotificationView { extension NotificationView.ViewModel { func bind(notificationView: NotificationView) { bindAuthor(notificationView: notificationView) - bindAuthorMenu(notificationView: notificationView) bindFollowRequest(notificationView: notificationView) - $context - .assign(to: \.context, on: notificationView.statusView.viewModel) - .store(in: &disposeBag) $authContext .assign(to: \.authContext, on: notificationView.statusView.viewModel) .store(in: &disposeBag) @@ -69,33 +55,6 @@ extension NotificationView.ViewModel { } private func bindAuthor(notificationView: NotificationView) { - // avatar - - $authorAvatarImageURL - .sink { url in - let configuration = 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 let formattedTimestamp = Publishers.CombineLatest( $timestamp, @@ -112,23 +71,6 @@ extension NotificationView.ViewModel { } .store(in: &disposeBag) - $visibility - .sink { visibility in - notificationView.visibilityIconImageView.image = visibility.image - } - .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) - Publishers.CombineLatest4( $authorName, $authorUsername, @@ -197,30 +139,6 @@ extension NotificationView.ViewModel { .store(in: &disposeBag) } - private func bindAuthorMenu(notificationView: NotificationView) { - Publishers.CombineLatest4( - $authorName, - $isMuting, - $isBlocking, - $isMyself - ) - .sink { [weak self] 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) - let (menu, actions) = notificationView.setupAuthorMenu(menuContext: menuContext) - notificationView.menuButton.menu = menu - notificationView.authorActions = actions - notificationView.menuButton.showsMenuAsPrimaryAction = true - - notificationView.menuButton.isHidden = menuContext.isMyself - } - .store(in: &disposeBag) - } - private func bindFollowRequest(notificationView: NotificationView) { Publishers.CombineLatest( $followRequestState, diff --git a/Mastodon/Scene/Notification/NotificationView/NotificationView.swift b/Mastodon/Scene/Notification/NotificationView/NotificationView.swift index 548ab0f04..3ba5c2bd1 100644 --- a/Mastodon/Scene/Notification/NotificationView/NotificationView.swift +++ b/Mastodon/Scene/Notification/NotificationView/NotificationView.swift @@ -181,7 +181,7 @@ public final class NotificationView: UIView { disposeBag.removeAll() viewModel.authContext = nil - viewModel.authorAvatarImageURL = nil + avatarButton.avatarImageView.image = nil avatarButton.avatarImageView.cancelTask() authorContainerViewBottomPaddingView.isHidden = true