diff --git a/Mastodon/Diffiable/Section/NotificationSection.swift b/Mastodon/Diffiable/Section/NotificationSection.swift index 9728c75be..63e88bdd6 100644 --- a/Mastodon/Diffiable/Section/NotificationSection.swift +++ b/Mastodon/Diffiable/Section/NotificationSection.swift @@ -30,7 +30,8 @@ extension NotificationSection { guard let dependency = dependency else { return nil } switch notificationItem { case .notification(let objectID, let attribute): - guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { + guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification, + !notification.isDeleted else { return UITableViewCell() } @@ -38,21 +39,21 @@ extension NotificationSection { cell.delegate = delegate // configure author - cell.avatarImageViewTask = Nuke.loadImage( - with: notification.account.avatarImageURL(), - options: ImageLoadingOptions( - placeholder: UIImage.placeholder(color: .systemFill), - transition: .fadeIn(duration: 0.2) - ), - into: cell.avatarImageView + cell.configure( + with: AvatarConfigurableViewConfiguration( + avatarImageURL: notification.account.avatarImageURL() + ) ) cell.actionImageView.image = UIImage( systemName: notification.notificationType.actionImageName, withConfiguration: UIImage.SymbolConfiguration( pointSize: 12, weight: .semibold ) - )?.withRenderingMode(.alwaysTemplate) - cell.actionImageBackground.backgroundColor = notification.notificationType.color + )? + .withRenderingMode(.alwaysTemplate) + .af.imageAspectScaled(toFit: CGSize(width: 14, height: 14)) + + cell.actionImageView.backgroundColor = notification.notificationType.color // configure author name, notification description, timestamp cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) diff --git a/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift b/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift index f0dbd2503..6a542bf2c 100644 --- a/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift +++ b/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift @@ -19,7 +19,7 @@ extension NotificationViewModel { ) { diffableDataSource = NotificationSection.tableViewDiffableDataSource( for: tableView, - managedObjectContext: context.managedObjectContext, + managedObjectContext: fetchedResultsController.managedObjectContext, delegate: delegate, dependency: dependency ) diff --git a/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift b/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift index 8075ce375..9567d6cbb 100644 --- a/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift +++ b/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift @@ -71,43 +71,44 @@ extension NotificationViewModel.LoadOldestState { sinceID: nil, minID: nil, limit: nil, - excludeTypes: [.followRequest], - accountID: nil) + excludeTypes: [], + accountID: nil + ) viewModel.context.apiService.allNotifications( domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox ) - .sink { completion in - switch completion { - case .failure(let error): - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch notification failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription) - case .finished: - // handle isFetchingLatestTimeline in fetch controller delegate - break + .sink { completion in + switch completion { + case .failure(let error): + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch notification failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription) + case .finished: + // handle isFetchingLatestTimeline in fetch controller delegate + break + } + + stateMachine.enter(Idle.self) + } receiveValue: { [weak viewModel] response in + guard let viewModel = viewModel else { return } + switch viewModel.selectedIndex.value { + case .EveryThing: + if response.value.isEmpty { + stateMachine.enter(NoMore.self) + } else { + stateMachine.enter(Idle.self) } - - stateMachine.enter(Idle.self) - } receiveValue: { [weak viewModel] response in - guard let viewModel = viewModel else { return } - switch viewModel.selectedIndex.value { - case .EveryThing: - if response.value.isEmpty { - stateMachine.enter(NoMore.self) - } else { - stateMachine.enter(Idle.self) - } - case .Mentions: - viewModel.noMoreNotification.value = response.value.isEmpty - let list = response.value.filter { $0.type == Mastodon.Entity.Notification.NotificationType.mention } - if list.isEmpty { - stateMachine.enter(NoMore.self) - } else { - stateMachine.enter(Idle.self) - } + case .Mentions: + viewModel.noMoreNotification.value = response.value.isEmpty + let list = response.value.filter { $0.type == Mastodon.Entity.Notification.NotificationType.mention } + if list.isEmpty { + stateMachine.enter(NoMore.self) + } else { + stateMachine.enter(Idle.self) } } - .store(in: &viewModel.disposeBag) + } + .store(in: &viewModel.disposeBag) } } diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift index ad6092ad5..f4987dc45 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift @@ -37,6 +37,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { static let actionImageBorderWidth: CGFloat = 2 static let statusPadding = UIEdgeInsets(top: 50, left: 73, bottom: 24, right: 24) + static let actionImageViewSize = CGSize(width: 24, height: 24) var disposeBag = Set() var pollCountdownSubscription: AnyCancellable? @@ -45,32 +46,26 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { var containerStackViewBottomLayoutConstraint: NSLayoutConstraint! let containerStackView = UIStackView() - var avatarImageViewTask: ImageTask? let avatarImageView: UIImageView = { let imageView = FLAnimatedImageView() - imageView.layer.cornerRadius = 4 - imageView.layer.cornerCurve = .continuous - imageView.clipsToBounds = true return imageView }() let actionImageView: UIImageView = { let imageView = UIImageView() + imageView.contentMode = .center imageView.tintColor = Asset.Colors.Background.systemBackground.color + imageView.isOpaque = true + imageView.layer.masksToBounds = true + imageView.layer.cornerRadius = NotificationStatusTableViewCell.actionImageViewSize.width * 0.5 + imageView.layer.cornerCurve = .circular + imageView.layer.borderWidth = NotificationStatusTableViewCell.actionImageBorderWidth + imageView.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor + imageView.layer.shouldRasterize = true + imageView.layer.rasterizationScale = UIScreen.main.scale return imageView }() - let actionImageBackground: UIView = { - let view = UIView() - view.layer.cornerRadius = (24 + NotificationStatusTableViewCell.actionImageBorderWidth) / 2 - view.layer.cornerCurve = .continuous - view.clipsToBounds = true - view.layer.borderWidth = NotificationStatusTableViewCell.actionImageBorderWidth - view.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor - view.tintColor = Asset.Colors.Background.systemBackground.color - return view - }() - let avatarContainer: UIView = { let view = UIView() return view @@ -148,8 +143,6 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { override func prepareForReuse() { super.prepareForReuse() isFiltered = false - avatarImageViewTask?.cancel() - avatarImageViewTask = nil statusView.updateContentWarningDisplay(isHidden: true, animated: false) statusView.pollTableView.dataSource = nil statusView.playerContainerView.reset() @@ -199,20 +192,13 @@ extension NotificationStatusTableViewCell { avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), ]) - actionImageBackground.translatesAutoresizingMaskIntoConstraints = false - avatarContainer.addSubview(actionImageBackground) - NSLayoutConstraint.activate([ - actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.centerYAnchor.constraint(equalTo: avatarImageView.bottomAnchor), - actionImageBackground.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor), - ]) - actionImageView.translatesAutoresizingMaskIntoConstraints = false - actionImageBackground.addSubview(actionImageView) + avatarContainer.addSubview(actionImageView) NSLayoutConstraint.activate([ - actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor), - actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor), + actionImageView.centerYAnchor.constraint(equalTo: avatarContainer.bottomAnchor), + actionImageView.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor), + actionImageView.widthAnchor.constraint(equalToConstant: NotificationStatusTableViewCell.actionImageViewSize.width), + actionImageView.heightAnchor.constraint(equalToConstant: NotificationStatusTableViewCell.actionImageViewSize.height), ]) containerStackView.addArrangedSubview(contentStackView) @@ -302,7 +288,7 @@ extension NotificationStatusTableViewCell { super.traitCollectionDidChange(previousTraitCollection) resetSeparatorLineLayout() - actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor + avatarImageView.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor } @@ -405,3 +391,11 @@ extension NotificationStatusTableViewCell { } } + +// MARK: - AvatarConfigurableView +extension NotificationStatusTableViewCell: AvatarConfigurableView { + static var configurableAvatarImageSize: CGSize { CGSize(width: 35, height: 35) } + static var configurableAvatarImageCornerRadius: CGFloat { 4 } + var configurableAvatarImageView: UIImageView? { avatarImageView } + var configurableAvatarButton: UIButton? { nil } +}