diff --git a/SignalServiceKit/src/Messages/EarlyMessageManager.swift b/SignalServiceKit/src/Messages/EarlyMessageManager.swift index e1d4992e52..7ca829e8c2 100644 --- a/SignalServiceKit/src/Messages/EarlyMessageManager.swift +++ b/SignalServiceKit/src/Messages/EarlyMessageManager.swift @@ -405,20 +405,18 @@ public class EarlyMessageManager: NSObject { case .messageReadOnLinkedDevice(let timestamp): Logger.info("Applying early read receipt from linked device for message \(identifier)") - OWSReceiptManager.shared.markAsReadOnLinkedDevice( + OWSReceiptManager.shared.markMessageAsReadOnLinkedDevice( message, - thread: message.thread(transaction: transaction), readTimestamp: timestamp, - transaction: transaction + tx: transaction ) case .messageViewedOnLinkedDevice(let timestamp): Logger.info("Applying early viewed receipt from linked device for message \(identifier)") - OWSReceiptManager.shared.markAsViewed( - onLinkedDevice: message, - thread: message.thread(transaction: transaction), + OWSReceiptManager.shared.markMessageAsViewedOnLinkedDevice( + message, viewedTimestamp: timestamp, - transaction: transaction + tx: transaction ) } } diff --git a/SignalServiceKit/src/Messages/OWSReceiptManager.h b/SignalServiceKit/src/Messages/OWSReceiptManager.h index 8959761a0e..9c4f03edfb 100644 --- a/SignalServiceKit/src/Messages/OWSReceiptManager.h +++ b/SignalServiceKit/src/Messages/OWSReceiptManager.h @@ -53,13 +53,6 @@ extern NSString *const kIncomingMessageMarkedAsReadNotification; - (instancetype)init NS_DESIGNATED_INITIALIZER; -#pragma mark - Linked Device Read Receipts - -- (void)markAsViewedOnLinkedDevice:(TSMessage *)message - thread:(TSThread *)thread - viewedTimestamp:(uint64_t)viewedTimestamp - transaction:(SDSAnyWriteTransaction *)transaction; - #pragma mark - Locally Read // This method can be called from any thread. diff --git a/SignalServiceKit/src/Messages/OWSReceiptManager.m b/SignalServiceKit/src/Messages/OWSReceiptManager.m index 1b73121e9f..6c159d5ff7 100644 --- a/SignalServiceKit/src/Messages/OWSReceiptManager.m +++ b/SignalServiceKit/src/Messages/OWSReceiptManager.m @@ -238,45 +238,6 @@ NSString *const OWSReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsEnabl [transaction addAsyncCompletionOffMain:^{ [self scheduleProcessing]; }]; } -#pragma mark - Linked Device Read Receipts - -- (void)markAsViewedOnLinkedDevice:(TSMessage *)message - thread:(TSThread *)thread - viewedTimestamp:(uint64_t)viewedTimestamp - transaction:(SDSAnyWriteTransaction *)transaction -{ - OWSAssertDebug(message); - OWSAssertDebug(thread); - OWSAssertDebug(transaction); - - if (message.giftBadge != nil) { - [message anyUpdateMessageWithTransaction:transaction - block:^(TSMessage *_Nonnull obj) { - if ([obj isKindOfClass:[TSIncomingMessage class]]) { - obj.giftBadge.redemptionState = OWSGiftBadgeRedemptionStateRedeemed; - } else if ([obj isKindOfClass:[TSOutgoingMessage class]]) { - obj.giftBadge.redemptionState = OWSGiftBadgeRedemptionStateOpened; - } else { - OWSFailDebug(@"Unexpected giftBadge message"); - } - }]; - return; - } - - if ([message isKindOfClass:[TSIncomingMessage class]]) { - TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message; - BOOL hasPendingMessageRequest = [thread hasPendingMessageRequestWithTransaction:transaction.unwrapGrdbRead]; - OWSReceiptCircumstance circumstance = hasPendingMessageRequest - ? OWSReceiptCircumstanceOnLinkedDeviceWhilePendingMessageRequest - : OWSReceiptCircumstanceOnLinkedDevice; - - [incomingMessage markAsViewedAtTimestamp:viewedTimestamp - thread:thread - circumstance:circumstance - transaction:transaction]; - } -} - #pragma mark - Settings - (void)prepareCachedValues diff --git a/SignalServiceKit/src/Messages/OWSReceiptManager.swift b/SignalServiceKit/src/Messages/OWSReceiptManager.swift index 0864b18627..e30cac89e7 100644 --- a/SignalServiceKit/src/Messages/OWSReceiptManager.swift +++ b/SignalServiceKit/src/Messages/OWSReceiptManager.swift @@ -339,7 +339,7 @@ public extension OWSReceiptManager { messageTimestamp: \.timestamp, tx: tx, markMessage: { - markAsReadOnLinkedDevice($0, thread: $0.thread(transaction: tx), readTimestamp: readTimestamp, transaction: tx) + markMessageAsReadOnLinkedDevice($0, readTimestamp: readTimestamp, tx: tx) }, markStoryMessage: { $0.markAsRead(at: readTimestamp, circumstance: .onLinkedDevice, transaction: tx) @@ -358,7 +358,7 @@ public extension OWSReceiptManager { messageTimestamp: \.timestamp, tx: tx, markMessage: { - markAsViewed(onLinkedDevice: $0, thread: $0.thread(transaction: tx), viewedTimestamp: viewedTimestamp, transaction: tx) + markMessageAsViewedOnLinkedDevice($0, viewedTimestamp: viewedTimestamp, tx: tx) }, markStoryMessage: { $0.markAsViewed(at: viewedTimestamp, circumstance: .onLinkedDevice, transaction: tx) @@ -498,57 +498,88 @@ public extension OWSReceiptManager { return readUniqueIds } - func markAsReadOnLinkedDevice( + func markMessageAsReadOnLinkedDevice( _ message: TSMessage, - thread: TSThread, readTimestamp: UInt64, - transaction: SDSAnyWriteTransaction + tx: SDSAnyWriteTransaction ) { - if let incomingMessage = message as? TSIncomingMessage { - let circumstance: OWSReceiptCircumstance - if thread.hasPendingMessageRequest(transaction: transaction.unwrapGrdbRead) { - circumstance = .onLinkedDeviceWhilePendingMessageRequest - } else { - circumstance = .onLinkedDevice - } - - // Always re-mark the message as read to ensure any earlier read - // time is applied to disappearing messages. Also mark any unread - // messages appearing earlier in the thread as read as well. - // - // Do not automatically clear notifications, as we will do so - // manually below. + switch message { + case let incomingMessage as TSIncomingMessage: + let thread = message.thread(transaction: tx) + let circumstance = linkedDeviceReceiptCircumstance(for: thread, tx: tx) + // Always re-mark the message as read to ensure any earlier read time is + // applied to disappearing messages. incomingMessage.markAsRead( atTimestamp: readTimestamp, thread: thread, circumstance: circumstance, + // Do not automatically clear notifications; we will do so below. shouldClearNotifications: false, - transaction: transaction + transaction: tx ) + // Also mark any unread messages appearing earlier in the thread as read. let markedAsReadIds = self.markAsRead( beforeSortId: incomingMessage.sortId, thread: thread, readTimestamp: readTimestamp, circumstance: circumstance, + // Do not automatically clear notifications; we will do so below. shouldClearNotifications: false, - transaction: transaction + transaction: tx ) - // Manually clear notifications for all the now-marked-read messages - // in one batch. + // Clear notifications for all the now-marked-read messages in one batch. notificationPresenter?.cancelNotifications(messageIds: [incomingMessage.uniqueId] + markedAsReadIds) - } else if let outgoingMessage = message as? TSOutgoingMessage { + case let outgoingMessage as TSOutgoingMessage: // Outgoing messages are always "read", but if we get a receipt // from our linked device about one that indicates that any reactions // we received on this message should also be marked read. - outgoingMessage.markUnreadReactionsAsRead(transaction: transaction) - } else { + outgoingMessage.markUnreadReactionsAsRead(transaction: tx) + default: owsFailDebug("Message was neither incoming nor outgoing!") } } + func markMessageAsViewedOnLinkedDevice(_ message: TSMessage, viewedTimestamp: UInt64, tx: SDSAnyWriteTransaction) { + if message.giftBadge != nil { + message.anyUpdateMessage(transaction: tx) { obj in + switch obj { + case let incomingMessage as TSIncomingMessage: + incomingMessage.giftBadge?.redemptionState = .redeemed + case let outgoingMessage as TSOutgoingMessage: + outgoingMessage.giftBadge?.redemptionState = .opened + default: + owsFailDebug("Unexpected giftBadge message") + } + } + return + } + + switch message { + case let incomingMessage as TSIncomingMessage: + let thread = message.thread(transaction: tx) + let circumstance = linkedDeviceReceiptCircumstance(for: thread, tx: tx) + incomingMessage.markAsViewed( + atTimestamp: viewedTimestamp, + thread: thread, + circumstance: circumstance, + transaction: tx + ) + default: + break + } + } + + private func linkedDeviceReceiptCircumstance(for thread: TSThread, tx: SDSAnyReadTransaction) -> OWSReceiptCircumstance { + if thread.hasPendingMessageRequest(transaction: tx.unwrapGrdbRead) { + return .onLinkedDeviceWhilePendingMessageRequest + } else { + return .onLinkedDevice + } + } + static func markAllCallInteractionsAsReadLocally( beforeSQLId sqlId: NSNumber?, /* Clears everything if nil */ thread: TSThread,