// // APIService+Notification.swift // Mastodon // // Created by sxiaojian on 2021/4/13. // import Combine import CoreData import CoreDataStack import Foundation import MastodonSDK import OSLog import class CoreDataStack.Notification extension APIService { func notifications( maxID: Mastodon.Entity.Status.ID?, scope: NotificationTimelineViewModel.Scope, authenticationBox: MastodonAuthenticationBox ) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Notification]> { let authorization = authenticationBox.userAuthorization let query = Mastodon.API.Notifications.Query( maxID: maxID, types: { switch scope { case .everything: return [ .follow, .followRequest, .mention, .reblog, .favourite, .poll, .status, ] case .mentions: return [ .mention, .status, ] } }(), excludeTypes: { switch scope { case .everything: return nil case .mentions: return [.follow, .followRequest, .reblog, .favourite, .poll] } }() ) let response = try await Mastodon.API.Notifications.getNotifications( session: session, domain: authenticationBox.domain, query: query, authorization: authorization ).singleOutput() let managedObjectContext = self.backgroundManagedObjectContext try await managedObjectContext.performChanges { guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user else { assertionFailure() return } var notifications: [Notification] = [] for entity in response.value { let result = Persistence.Notification.createOrMerge( in: managedObjectContext, context: Persistence.Notification.PersistContext( domain: authenticationBox.domain, entity: entity, me: me, networkDate: response.networkDate ) ) notifications.append(result.notification) } // locate anchor notification let anchorNotification: Notification? = { guard let maxID = query.maxID else { return nil } let request = Notification.sortedFetchRequest request.predicate = Notification.predicate( domain: authenticationBox.domain, userID: authenticationBox.userID, id: maxID ) request.fetchLimit = 1 return try? managedObjectContext.fetch(request).first }() // update hasMore flag for anchor status let acct = Feed.Acct.mastodon(domain: authenticationBox.domain, userID: authenticationBox.userID) let kind: Feed.Kind = scope == .everything ? .notificationAll : .notificationMentions if let anchorNotification = anchorNotification, let feed = anchorNotification.feed(kind: kind, acct: acct) { feed.update(hasMore: false) } // persist Feed relationship let sortedNotifications = notifications.sorted(by: { $0.createAt < $1.createAt }) let oldestNotification = sortedNotifications.first for notification in notifications { let _feed = notification.feed(kind: kind, acct: acct) if let feed = _feed { feed.update(updatedAt: response.networkDate) } else { let feedProperty = Feed.Property( acct: acct, kind: kind, hasMore: false, createdAt: notification.createAt, updatedAt: response.networkDate ) let feed = Feed.insert(into: managedObjectContext, property: feedProperty) notification.attach(feed: feed) // set hasMore on oldest notification if is new feed if notification === oldestNotification { feed.update(hasMore: true) } } } } return response } } extension APIService { func notification( notificationID: Mastodon.Entity.Notification.ID, authenticationBox: MastodonAuthenticationBox ) async throws -> Mastodon.Response.Content { let domain = authenticationBox.domain let authorization = authenticationBox.userAuthorization let response = try await Mastodon.API.Notifications.getNotification( session: session, domain: domain, notificationID: notificationID, authorization: authorization ).singleOutput() let managedObjectContext = self.backgroundManagedObjectContext try await managedObjectContext.performChanges { guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user else { return } _ = Persistence.Notification.createOrMerge( in: managedObjectContext, context: Persistence.Notification.PersistContext( domain: domain, entity: response.value, me: me, networkDate: response.networkDate ) ) } return response } }