fix: timer update leaking raise crash in notification scene

This commit is contained in:
CMK 2021-06-15 16:36:42 +08:00
parent 389d0971cd
commit 7d1c150364
5 changed files with 51 additions and 36 deletions

View File

@ -101,6 +101,10 @@ extension MastodonNotification {
]) ])
} }
} }
public static func predicate(validTypesRaws types: [String]) -> NSPredicate {
return NSPredicate(format: "%K IN %@", #keyPath(MastodonNotification.typeRaw), types)
}
} }

View File

@ -30,14 +30,16 @@ extension NotificationSection {
guard let dependency = dependency else { return nil } guard let dependency = dependency else { return nil }
switch notificationItem { switch notificationItem {
case .notification(let objectID, let attribute): case .notification(let objectID, let attribute):
let notification = managedObjectContext.object(with: objectID) as! MastodonNotification let notification = managedObjectContext.object(with: objectID) as! MastodonNotification
guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else { guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else {
// filter out invalid type using predicate
assertionFailure() assertionFailure()
return nil return UITableViewCell()
} }
let timeText = notification.createAt.slowedTimeAgoSinceNow
let createAt = notification.createAt
let timeText = createAt.slowedTimeAgoSinceNow
let actionText = type.actionText let actionText = type.actionText
let actionImageName = type.actionImageName let actionImageName = type.actionImageName
let color = type.color let color = type.color
@ -57,23 +59,24 @@ extension NotificationSection {
requestUserID: requestUserID, requestUserID: requestUserID,
statusItemAttribute: attribute statusItemAttribute: attribute
) )
cell.actionImageBackground.backgroundColor = color
cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName
cell.actionLabel.text = actionText + " · " + timeText
timestampUpdatePublisher timestampUpdatePublisher
.sink { _ in .sink { [weak cell] _ in
let timeText = notification.createAt.slowedTimeAgoSinceNow guard let cell = cell else { return }
let timeText = createAt.slowedTimeAgoSinceNow
cell.actionLabel.text = actionText + " · " + timeText cell.actionLabel.text = actionText + " · " + timeText
} }
.store(in: &cell.disposeBag) .store(in: &cell.disposeBag)
cell.actionImageBackground.backgroundColor = color
cell.actionLabel.text = actionText + " · " + timeText
cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName
if let url = notification.account.avatarImageURL() { if let url = notification.account.avatarImageURL() {
cell.avatatImageView.af.setImage( cell.avatarImageView.af.setImage(
withURL: url, withURL: url,
placeholderImage: UIImage.placeholder(color: .systemFill), placeholderImage: UIImage.placeholder(color: .systemFill),
imageTransition: .crossDissolve(0.2) imageTransition: .crossDissolve(0.2)
) )
} }
cell.avatatImageView.gesture().sink { [weak cell] _ in cell.avatarImageView.gesture().sink { [weak cell] _ in
cell?.delegate?.userAvatarDidPressed(notification: notification) cell?.delegate?.userAvatarDidPressed(notification: notification)
} }
.store(in: &cell.disposeBag) .store(in: &cell.disposeBag)
@ -86,8 +89,9 @@ extension NotificationSection {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell
cell.delegate = delegate cell.delegate = delegate
timestampUpdatePublisher timestampUpdatePublisher
.sink { _ in .sink { [weak cell] _ in
let timeText = notification.createAt.slowedTimeAgoSinceNow guard let cell = cell else { return }
let timeText = createAt.slowedTimeAgoSinceNow
cell.actionLabel.text = actionText + " · " + timeText cell.actionLabel.text = actionText + " · " + timeText
} }
.store(in: &cell.disposeBag) .store(in: &cell.disposeBag)

View File

@ -9,6 +9,7 @@ import CoreData
import CoreDataStack import CoreDataStack
import os.log import os.log
import UIKit import UIKit
import MastodonSDK
extension NotificationViewModel { extension NotificationViewModel {
func setupDiffableDataSource( func setupDiffableDataSource(
@ -16,7 +17,7 @@ extension NotificationViewModel {
delegate: NotificationTableViewCellDelegate, delegate: NotificationTableViewCellDelegate,
dependency: NeedsDependency dependency: NeedsDependency
) { ) {
let timestampUpdatePublisher = Timer.publish(every: 30.0, on: .main, in: .common) let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect() .autoconnect()
.share() .share()
.eraseToAnyPublisher() .eraseToAnyPublisher()
@ -44,7 +45,14 @@ extension NotificationViewModel: NSFetchedResultsControllerDelegate {
guard let diffableDataSource = self.diffableDataSource else { return } guard let diffableDataSource = self.diffableDataSource else { return }
let predicate = fetchedResultsController.fetchRequest.predicate let predicate: NSPredicate = {
let notificationTypePredicate = MastodonNotification.predicate(
validTypesRaws: Mastodon.Entity.Notification.NotificationType.knownCases.map { $0.rawValue }
)
return fetchedResultsController.fetchRequest.predicate.flatMap {
NSCompoundPredicate(andPredicateWithSubpredicates: [$0, notificationTypePredicate])
} ?? notificationTypePredicate
}()
let parentManagedObjectContext = fetchedResultsController.managedObjectContext let parentManagedObjectContext = fetchedResultsController.managedObjectContext
let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.parent = parentManagedObjectContext managedObjectContext.parent = parentManagedObjectContext
@ -73,19 +81,6 @@ extension NotificationViewModel: NSFetchedResultsControllerDelegate {
newSnapshot.appendSections([.main]) newSnapshot.appendSections([.main])
let items: [NotificationItem] = notifications.map { notification in let items: [NotificationItem] = notifications.map { notification in
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute() let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
// let attribute: Item.StatusAttribute = {
// if let attribute = oldSnapshotAttributeDict[notification.objectID] {
// return attribute
// } else if let status = notification.status {
// let attribute = Item.StatusAttribute()
// let isSensitive = status.sensitive || !(status.spoilerText ?? "").isEmpty
// attribute.isRevealing.value = !isSensitive
// return attribute
// } else {
// return Item.StatusAttribute()
// }
// }()
return NotificationItem.notification(objectID: notification.objectID, attribute: attribute) return NotificationItem.notification(objectID: notification.objectID, attribute: attribute)
} }
newSnapshot.appendItems(items, toSection: .main) newSnapshot.appendItems(items, toSection: .main)

View File

@ -17,7 +17,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
var pollCountdownSubscription: AnyCancellable? var pollCountdownSubscription: AnyCancellable?
var delegate: NotificationTableViewCellDelegate? var delegate: NotificationTableViewCellDelegate?
let avatatImageView: UIImageView = { let avatarImageView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.layer.cornerRadius = 4 imageView.layer.cornerRadius = 4
imageView.layer.cornerCurve = .continuous imageView.layer.cornerCurve = .continuous
@ -86,7 +86,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
avatatImageView.af.cancelImageRequest() avatarImageView.af.cancelImageRequest()
statusView.updateContentWarningDisplay(isHidden: true, animated: false) statusView.updateContentWarningDisplay(isHidden: true, animated: false)
statusView.pollTableView.dataSource = nil statusView.pollTableView.dataSource = nil
statusView.playerContainerView.reset() statusView.playerContainerView.reset()
@ -142,13 +142,13 @@ extension NotificationStatusTableViewCell {
avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1) avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1)
]) ])
avatarContainer.addSubview(avatatImageView) avatarContainer.addSubview(avatarImageView)
avatatImageView.translatesAutoresizingMaskIntoConstraints = false avatarImageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
avatatImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1),
avatatImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1),
avatatImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor),
avatatImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor)
]) ])
avatarContainer.addSubview(actionImageBackground) avatarContainer.addSubview(actionImageBackground)

View File

@ -49,6 +49,18 @@ extension Mastodon.Entity.Notification {
case _other(String) case _other(String)
public static var knownCases: [NotificationType] {
return [
.follow,
.followRequest,
.mention,
.reblog,
.favourite,
.poll,
.status
]
}
public init?(rawValue: String) { public init?(rawValue: String) {
switch rawValue { switch rawValue {
case "follow": self = .follow case "follow": self = .follow