@@ -159,7 +159,19 @@ class NotificationSyncMediator {
159159 /// - completion: Callback to be executed on completion.
160160 ///
161161 func markAsRead( _ notification: Notification , completion: ( ( Error ? ) -> Void ) ? = nil ) {
162- mark ( notification, asRead: true , completion: completion)
162+ mark ( [ notification] , asRead: true , completion: completion)
163+ }
164+
165+ /// Marks an array of notifications as Read.
166+ ///
167+ /// - Note: This method should only be used on the main thread.
168+ ///
169+ /// - Parameters:
170+ /// - notifications: Notifications that were marked as read.
171+ /// - completion: Callback to be executed on completion.
172+ ///
173+ func markAsRead( _ notifications: [ Notification ] , completion: ( ( Error ? ) -> Void ) ? = nil ) {
174+ mark ( notifications, asRead: true , completion: completion)
163175 }
164176
165177 /// Marks a Notification as Unead.
@@ -171,17 +183,19 @@ class NotificationSyncMediator {
171183 /// - completion: Callback to be executed on completion.
172184 ///
173185 func markAsUnread( _ notification: Notification , completion: ( ( Error ? ) -> Void ) ? = nil ) {
174- mark ( notification, asRead: false , completion: completion)
186+ mark ( [ notification] , asRead: false , completion: completion)
175187 }
176188
177- private func mark( _ notification : Notification , asRead read: Bool = true , completion: ( ( Error ? ) -> Void ) ? = nil ) {
189+ private func mark( _ notifications : [ Notification ] , asRead read: Bool = true , completion: ( ( Error ? ) -> Void ) ? = nil ) {
178190 assert ( Thread . isMainThread)
179191
180- let noteID = notification. notificationId
181- remote. updateReadStatus ( noteID, read: read) { error in
192+ let noteIDs = notifications. map {
193+ $0. notificationId
194+ }
195+
196+ remote. updateReadStatusForNotifications ( noteIDs, read: read) { error in
182197 if let error = error {
183- let readState = read ? " read " : " unread "
184- DDLogError ( " Error marking note as \( readState) : \( error) " )
198+ DDLogError ( " Error marking notifications as \( Self . readState ( for: read) ) : \( error) " )
185199 // Ideally, we'd want to revert to the previous status if this
186200 // fails, but if the note is visible, the UI layer will keep
187201 // trying to mark this note and fail.
@@ -191,13 +205,24 @@ class NotificationSyncMediator {
191205 // next successful sync.
192206 //
193207 // https://github.com/wordpress-mobile/WordPress-iOS/issues/7216
194- NotificationSyncMediator ( ) ? . invalidateCacheForNotification ( with : noteID )
208+ NotificationSyncMediator ( ) ? . invalidateCacheForNotifications ( noteIDs )
195209 }
196210
197211 completion ? ( error)
198212 }
199213
200- updateReadStatus ( read, forNoteWithObjectID: notification. objectID)
214+ let objectIDs = notifications. map {
215+ $0. objectID
216+ }
217+
218+ updateReadStatus (
219+ read,
220+ forNotesWithObjectIDs: objectIDs
221+ )
222+ }
223+
224+ private static func readState( for read: Bool ) -> String {
225+ read ? " read " : " unread "
201226 }
202227
203228 /// Invalidates the cache for a notification, marks it as read and syncs it.
@@ -207,7 +232,7 @@ class NotificationSyncMediator {
207232 /// - completion: Callback to be executed on completion.
208233 ///
209234 func markAsReadAndSync( _ noteID: String , completion: ( ( Error ? ) -> Void ) ? = nil ) {
210- invalidateCacheForNotification ( with : noteID)
235+ invalidateCacheForNotification ( noteID)
211236 remote. updateReadStatus ( noteID, read: true ) { error in
212237 if let error = error {
213238 DDLogError ( " Error marking note as read: \( error) " )
@@ -256,16 +281,20 @@ class NotificationSyncMediator {
256281
257282 /// Invalidates the local cache for the notification with the specified ID.
258283 ///
259- func invalidateCacheForNotification( with noteID: String ) {
284+ func invalidateCacheForNotification( _ noteID: String ) {
285+ invalidateCacheForNotifications ( [ noteID] )
286+ }
287+
288+ /// Invalidates the local cache for all the notifications with specified ID's in the array.
289+ ///
290+ func invalidateCacheForNotifications( _ noteIDs: [ String ] ) {
260291 let derivedContext = type ( of: self ) . sharedDerivedContext ( with: contextManager)
261- let predicate = NSPredicate ( format: " (notificationId == %@) " , noteID)
262292
263293 derivedContext. perform {
264- guard let notification = derivedContext. firstObject ( ofType: Notification . self, matching: predicate) else {
265- return
266- }
294+ let predicate = NSPredicate ( format: " (notificationId IN %@) " , noteIDs)
295+ let notifications = derivedContext. allObjects ( ofType: Notification . self, matching: predicate)
267296
268- notification . notificationHash = nil
297+ notifications . forEach { $0 . notificationHash = nil }
269298
270299 self . contextManager. save ( derivedContext)
271300 }
@@ -373,8 +402,24 @@ private extension NotificationSyncMediator {
373402 /// - noteObjectID: CoreData ObjectID
374403 ///
375404 func updateReadStatus( _ status: Bool , forNoteWithObjectID noteObjectID: NSManagedObjectID ) {
376- let note = mainContext. loadObject ( ofType: Notification . self, with: noteObjectID)
377- note? . read = status
405+ updateReadStatus ( status, forNotesWithObjectIDs: [ noteObjectID] )
406+ }
407+
408+ /// Updates the Read status, of an array of Notifications, as specified.
409+ ///
410+ /// Note: This method uses *saveContextAndWait* in order to prevent animation glitches when pushing
411+ /// Notification Details.
412+ ///
413+ /// - Parameters:
414+ /// - status: New *read* value
415+ /// - notesObjectIDs: CoreData ObjectIDs
416+ ///
417+ func updateReadStatus( _ status: Bool , forNotesWithObjectIDs notesObjectIDs: [ NSManagedObjectID ] ) {
418+ let predicate = NSPredicate ( format: " SELF IN %@ " , notesObjectIDs)
419+
420+ let notes = mainContext. allObjects ( ofType: Notification . self, matching: predicate)
421+
422+ notes. forEach { $0. read = status }
378423 contextManager. saveContextAndWait ( mainContext)
379424 }
380425
0 commit comments