diff --git a/Podfile b/Podfile index 485fd48eac..293a88c8f9 100644 --- a/Podfile +++ b/Podfile @@ -9,7 +9,7 @@ def shared_pods pod 'SQLCipher', :git => 'https://github.com/sqlcipher/sqlcipher.git', :commit => 'd5c2bec' # pod 'YapDatabase/SQLCipher', path: '../YapDatabase' pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release' - pod 'AxolotlKit', git: 'https://github.com/signalapp/SignalProtocolKit.git' + pod 'AxolotlKit', git: 'https://github.com/signalapp/SignalProtocolKit.git', branch: 'release/2.30.2' # pod 'AxolotlKit', path: '../SignalProtocolKit' pod 'SignalServiceKit', path: '.', testspecs: ["Tests"] pod 'HKDFKit', git: 'https://github.com/signalapp/HKDFKit.git' diff --git a/Podfile.lock b/Podfile.lock index 7d5c582548..e2be611eea 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -152,7 +152,7 @@ PODS: DEPENDENCIES: - AFNetworking - - AxolotlKit (from `https://github.com/signalapp/SignalProtocolKit.git`) + - AxolotlKit (from `https://github.com/signalapp/SignalProtocolKit.git`, branch `release/2.30.2`) - Curve25519Kit (from `https://github.com/signalapp/Curve25519Kit`) - GRKOpenSSLFramework (from `https://github.com/signalapp/GRKOpenSSLFramework`) - HKDFKit (from `https://github.com/signalapp/HKDFKit.git`) @@ -183,6 +183,7 @@ SPEC REPOS: EXTERNAL SOURCES: AxolotlKit: + :branch: release/2.30.2 :git: https://github.com/signalapp/SignalProtocolKit.git Curve25519Kit: :git: https://github.com/signalapp/Curve25519Kit @@ -204,7 +205,7 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: AxolotlKit: - :commit: 7b2af262204af6f5dcdc2986571c5a72400f7e04 + :commit: 446fbdaa5760cf1b1337441340d4d885beb27beb :git: https://github.com/signalapp/SignalProtocolKit.git Curve25519Kit: :commit: a4f46bd621bb2bcbf1a44b360e8ac4209e410ee9 @@ -246,6 +247,6 @@ SPEC CHECKSUMS: YapDatabase: b418a4baa6906e8028748938f9159807fd039af4 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 -PODFILE CHECKSUM: 40c4fc7dfb6066c4fdb80bc08600096e172dd4d7 +PODFILE CHECKSUM: 99cf978c46911aa96f569649e7113245f4ed30b7 COCOAPODS: 1.5.3 diff --git a/Pods b/Pods index e904760167..4c450a8a17 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit e90476016714371cc689f6fae35fe853a0ad16ae +Subproject commit 4c450a8a1702d3b00eaa476dbddff487d3a91c53 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index ad158c3452..78c9fa0ffb 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -438,7 +438,6 @@ 4C63CC00210A620B003AE45C /* SignalTSan.supp in Resources */ = {isa = PBXBuildFile; fileRef = 4C63CBFF210A620B003AE45C /* SignalTSan.supp */; }; 4C6F527C20FFE8400097DEEE /* SignalUBSan.supp in Resources */ = {isa = PBXBuildFile; fileRef = 4C6F527B20FFE8400097DEEE /* SignalUBSan.supp */; }; 4C858A52212DC5E1001B45D3 /* UIImage+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C858A51212DC5E1001B45D3 /* UIImage+OWS.swift */; }; - 4C858A562130CBEC001B45D3 /* OWS110SortIdMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C858A552130CBEC001B45D3 /* OWS110SortIdMigration.swift */; }; 4C948FF72146EB4800349F0D /* BlockListCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C948FF62146EB4800349F0D /* BlockListCache.swift */; }; 4CA5F793211E1F06008C2708 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5F792211E1F06008C2708 /* Toast.swift */; }; 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; }; @@ -1125,7 +1124,6 @@ 4C63CBFF210A620B003AE45C /* SignalTSan.supp */ = {isa = PBXFileReference; lastKnownFileType = text; path = SignalTSan.supp; sourceTree = ""; }; 4C6F527B20FFE8400097DEEE /* SignalUBSan.supp */ = {isa = PBXFileReference; lastKnownFileType = text; path = SignalUBSan.supp; sourceTree = ""; }; 4C858A51212DC5E1001B45D3 /* UIImage+OWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+OWS.swift"; sourceTree = ""; }; - 4C858A552130CBEC001B45D3 /* OWS110SortIdMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWS110SortIdMigration.swift; sourceTree = ""; }; 4C948FF62146EB4800349F0D /* BlockListCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListCache.swift; sourceTree = ""; }; 4CA5F792211E1F06008C2708 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = ""; }; @@ -1634,7 +1632,6 @@ 346129E41FD5C0C600532771 /* OWSDatabaseMigrationRunner.m */, 34ABB2C32090C59700C727A6 /* OWSResaveCollectionDBMigration.h */, 34ABB2C22090C59600C727A6 /* OWSResaveCollectionDBMigration.m */, - 4C858A552130CBEC001B45D3 /* OWS110SortIdMigration.swift */, ); path = migrations; sourceTree = ""; @@ -3182,7 +3179,6 @@ 34AC09E6211B39B100997B47 /* SelectRecipientViewController.m in Sources */, 4C858A52212DC5E1001B45D3 /* UIImage+OWS.swift in Sources */, 34480B671FD0AA9400BC14EF /* UIFont+OWS.m in Sources */, - 4C858A562130CBEC001B45D3 /* OWS110SortIdMigration.swift in Sources */, 346129E61FD5C0C600532771 /* OWSDatabaseMigrationRunner.m in Sources */, 34AC0A11211B39EA00997B47 /* OWSLayerView.swift in Sources */, 34AC0A1B211B39EA00997B47 /* GradientView.swift in Sources */, diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index be831af502..a2916d6111 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.30.1 + 2.30.2 CFBundleSignature ???? CFBundleURLTypes @@ -38,7 +38,7 @@ CFBundleVersion - 2.30.1.1 + 2.30.2.4 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/Signal/src/Jobs/SessionResetJob.swift b/Signal/src/Jobs/SessionResetJob.swift index fa8fc07259..3d560c8014 100644 --- a/Signal/src/Jobs/SessionResetJob.swift +++ b/Signal/src/Jobs/SessionResetJob.swift @@ -30,7 +30,6 @@ public class SessionResetJob: NSObject { self.primaryStorage.deleteAllSessions(forContact: self.recipientId, protocolContext: transaction) DispatchQueue.main.async { - // MJK TODO - should be safe to remove this senderTimestamp let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.thread) self.messageSender.enqueue(endSessionMessage, success: { @@ -41,7 +40,6 @@ public class SessionResetJob: NSObject { self.primaryStorage.archiveAllSessions(forContact: self.recipientId, protocolContext: transaction) } Logger.info("successfully sent EndSessionMessage.") - // MJK TODO - should be safe to remove this senderTimestamp let message = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.thread, messageType: TSInfoMessageType.typeSessionDidEnd) diff --git a/Signal/src/Models/MessageActions.swift b/Signal/src/Models/MessageActions.swift index f4471d71a6..48785f6527 100644 --- a/Signal/src/Models/MessageActions.swift +++ b/Signal/src/Models/MessageActions.swift @@ -67,66 +67,67 @@ struct MessageActionBuilder { } } -extension ConversationViewItem { +@objc +class ConversationViewItemActions: NSObject { @objc - func textActions(delegate: MessageActionsDelegate) -> [MenuAction] { + class func textActions(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> [MenuAction] { var actions: [MenuAction] = [] - let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate) + let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(replyAction) - if self.hasBodyTextActionContent { - let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate) + if conversationViewItem.hasBodyTextActionContent { + let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(copyTextAction) } - let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate) + let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(deleteAction) - let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate) + let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(showDetailsAction) return actions } @objc - func mediaActions(delegate: MessageActionsDelegate) -> [MenuAction] { + class func mediaActions(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> [MenuAction] { var actions: [MenuAction] = [] - let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate) + let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(replyAction) - if self.hasMediaActionContent { - let copyMediaAction = MessageActionBuilder.copyMedia(conversationViewItem: self, delegate: delegate) + if conversationViewItem.hasMediaActionContent { + let copyMediaAction = MessageActionBuilder.copyMedia(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(copyMediaAction) - if self.canSaveMedia() { - let saveMediaAction = MessageActionBuilder.saveMedia(conversationViewItem: self, delegate: delegate) + if conversationViewItem.canSaveMedia() { + let saveMediaAction = MessageActionBuilder.saveMedia(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(saveMediaAction) } } - let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate) + let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(deleteAction) - let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate) + let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(showDetailsAction) return actions } @objc - func quotedMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] { - let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate) - let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate) - let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate) + class func quotedMessageActions(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> [MenuAction] { + let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) + let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) + let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) return [replyAction, deleteAction, showDetailsAction] } @objc - func infoMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] { - let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate) + class func infoMessageActions(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> [MenuAction] { + let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: conversationViewItem, delegate: delegate) return [deleteAction] } diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index f735d9fe26..c37dc03d9b 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -9,6 +9,7 @@ #import "AppSettingsViewController.h" #import "ContactCellView.h" #import "ContactTableViewCell.h" +#import "ConversationViewCell.h" #import "ConversationViewItem.h" #import "DateUtil.h" #import "DebugUIPage.h" @@ -21,6 +22,7 @@ #import "NotificationsManager.h" #import "OWSAddToContactViewController.h" #import "OWSAnyTouchGestureRecognizer.h" +#import "OWSAudioMessageView.h" #import "OWSAudioPlayer.h" #import "OWSBackup.h" #import "OWSBackupIO.h" diff --git a/Signal/src/ViewControllers/ColorPickerViewController.swift b/Signal/src/ViewControllers/ColorPickerViewController.swift index b3cb1a631e..b6978b9564 100644 --- a/Signal/src/ViewControllers/ColorPickerViewController.swift +++ b/Signal/src/ViewControllers/ColorPickerViewController.swift @@ -75,18 +75,18 @@ class ColorPicker: NSObject, ColorPickerViewDelegate { @objc let sheetViewController: SheetViewController - private let currentConversationColor: OWSConversationColor - @objc - init(currentConversationColor: OWSConversationColor) { - self.currentConversationColor = currentConversationColor + init(thread: TSThread) { + let colorName = thread.conversationColorName + let currentConversationColor = OWSConversationColor.conversationColorOrDefault(colorName: colorName) sheetViewController = SheetViewController() super.init() - let colorPickerView = ColorPickerView() + let colorPickerView = ColorPickerView(thread: thread) colorPickerView.delegate = self colorPickerView.select(conversationColor: currentConversationColor) + sheetViewController.contentView.addSubview(colorPickerView) colorPickerView.autoPinEdgesToSuperviewEdges() } @@ -105,21 +105,42 @@ protocol ColorPickerViewDelegate: class { class ColorPickerView: UIView, ColorViewDelegate { private let colorViews: [ColorView] + let conversationStyle: ConversationStyle + var outgoingMessageView = OWSMessageBubbleView(forAutoLayout: ()) + var incomingMessageView = OWSMessageBubbleView(forAutoLayout: ()) weak var delegate: ColorPickerViewDelegate? - override init(frame: CGRect) { + // This is mostly a developer convenience - OWSMessageCell asserts at some point + // that the available method width is greater than 0. + // We ultimately use the width of the picker view which will be larger. + let kMinimumConversationWidth: CGFloat = 300 + override var bounds: CGRect { + didSet { + updateMockConversationView() + } + } + + let mockConversationView: UIView = UIView() + + init(thread: TSThread) { let allConversationColors = OWSConversationColor.conversationColorNames.map { OWSConversationColor.conversationColorOrDefault(colorName: $0) } self.colorViews = allConversationColors.map { ColorView(conversationColor: $0) } - super.init(frame: frame) + self.conversationStyle = ConversationStyle(thread: thread) + + super.init(frame: .zero) colorViews.forEach { $0.delegate = self } let headerView = self.buildHeaderView() + mockConversationView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + mockConversationView.backgroundColor = Theme.backgroundColor + self.updateMockConversationView() + let paletteView = self.buildPaletteView(colorViews: colorViews) - let rowsStackView = UIStackView(arrangedSubviews: [headerView, paletteView]) + let rowsStackView = UIStackView(arrangedSubviews: [headerView, mockConversationView, paletteView]) rowsStackView.axis = .vertical addSubview(rowsStackView) rowsStackView.autoPinEdgesToSuperviewEdges() @@ -134,6 +155,7 @@ class ColorPickerView: UIView, ColorViewDelegate { func colorViewWasTapped(_ colorView: ColorView) { self.select(conversationColor: colorView.conversationColor) self.delegate?.colorPickerView(self, didPickConversationColor: colorView.conversationColor) + updateMockConversationView() } fileprivate func select(conversationColor selectedConversationColor: OWSConversationColor) { @@ -166,9 +188,59 @@ class ColorPickerView: UIView, ColorViewDelegate { return headerView } + private func updateMockConversationView() { + conversationStyle.viewWidth = max(bounds.size.width, kMinimumConversationWidth) + mockConversationView.subviews.forEach { $0.removeFromSuperview() } + + // outgoing + outgoingMessageView = OWSMessageBubbleView(forAutoLayout: ()) + let outgoingItem = MockConversationViewItem() + let outgoingText = NSLocalizedString("COLOR_PICKER_DEMO_MESSAGE_1", comment: "The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble.") + outgoingItem.interaction = MockOutgoingMessage(messageBody: outgoingText) + outgoingItem.displayableBodyText = DisplayableText.displayableText(outgoingText) + outgoingItem.interactionType = .outgoingMessage + + outgoingMessageView.viewItem = outgoingItem + outgoingMessageView.cellMediaCache = NSCache() + outgoingMessageView.conversationStyle = conversationStyle + outgoingMessageView.configureViews() + outgoingMessageView.loadContent() + let outgoingCell = UIView() + outgoingCell.addSubview(outgoingMessageView) + outgoingMessageView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .leading) + let outgoingSize = outgoingMessageView.measureSize() + outgoingMessageView.autoSetDimensions(to: outgoingSize) + + // incoming + incomingMessageView = OWSMessageBubbleView(forAutoLayout: ()) + let incomingItem = MockConversationViewItem() + let incomingText = NSLocalizedString("COLOR_PICKER_DEMO_MESSAGE_2", comment: "The second of two messages demonstrating the chosen conversation color, by rendering this message in an incoming message bubble.") + incomingItem.interaction = MockIncomingMessage(messageBody: incomingText) + incomingItem.displayableBodyText = DisplayableText.displayableText(incomingText) + incomingItem.interactionType = .incomingMessage + + incomingMessageView.viewItem = incomingItem + incomingMessageView.cellMediaCache = NSCache() + incomingMessageView.conversationStyle = conversationStyle + incomingMessageView.configureViews() + incomingMessageView.loadContent() + let incomingCell = UIView() + incomingCell.addSubview(incomingMessageView) + incomingMessageView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .trailing) + let incomingSize = incomingMessageView.measureSize() + incomingMessageView.autoSetDimensions(to: incomingSize) + + let messagesStackView = UIStackView(arrangedSubviews: [outgoingCell, incomingCell]) + messagesStackView.axis = .vertical + messagesStackView.spacing = 12 + + mockConversationView.addSubview(messagesStackView) + messagesStackView.autoPinEdgesToSuperviewMargins() + } + private func buildPaletteView(colorViews: [ColorView]) -> UIView { let paletteView = UIView() - paletteView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + paletteView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 0, right: 16) let kRowLength = 4 let rows: [UIView] = colorViews.chunked(by: kRowLength).map { colorViewsInRow in @@ -178,10 +250,210 @@ class ColorPickerView: UIView, ColorViewDelegate { } let rowsStackView = UIStackView(arrangedSubviews: rows) rowsStackView.axis = .vertical - rowsStackView.spacing = ScaleFromIPhone5To7Plus(16, 50) + rowsStackView.spacing = ScaleFromIPhone5To7Plus(12, 50) paletteView.addSubview(rowsStackView) rowsStackView.ows_autoPinToSuperviewMargins() + + // no-op gesture to keep taps from dismissing SheetView + paletteView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: nil)) return paletteView } } + +// MARK: Mock Classes for rendering demo conversation + +@objc +private class MockConversationViewItem: NSObject, ConversationViewItem { + var interaction: TSInteraction = TSMessage() + var interactionType: OWSInteractionType = OWSInteractionType.unknown + var quotedReply: OWSQuotedReplyModel? + var isGroupThread: Bool = false + var hasBodyText: Bool = true + var isQuotedReply: Bool = false + var hasQuotedAttachment: Bool = false + var hasQuotedText: Bool = false + var hasCellHeader: Bool = false + var isExpiringMessage: Bool = false + var shouldShowDate: Bool = false + var shouldShowSenderAvatar: Bool = false + var senderName: NSAttributedString? + var shouldHideFooter: Bool = false + var isFirstInCluster: Bool = true + var isLastInCluster: Bool = true + var unreadIndicator: OWSUnreadIndicator? + var lastAudioMessageView: OWSAudioMessageView? + var audioDurationSeconds: CGFloat = 0 + var audioProgressSeconds: CGFloat = 0 + var messageCellType: OWSMessageCellType = .textMessage + var displayableBodyText: DisplayableText? + var attachmentStream: TSAttachmentStream? + var attachmentPointer: TSAttachmentPointer? + var mediaSize: CGSize = .zero + var displayableQuotedText: DisplayableText? + var quotedAttachmentMimetype: String? + var quotedRecipientId: String? + var didCellMediaFailToLoad: Bool = false + var contactShare: ContactShareViewModel? + var systemMessageText: String? + var authorConversationColorName: String? + var hasBodyTextActionContent: Bool = false + var hasMediaActionContent: Bool = false + + override init() { + super.init() + } + + func dequeueCell(for collectionView: UICollectionView, indexPath: IndexPath) -> ConversationViewCell { + owsFailDebug("unexpected invocation") + return ConversationViewCell(forAutoLayout: ()) + } + + func replace(_ interaction: TSInteraction, transaction: YapDatabaseReadTransaction) { + owsFailDebug("unexpected invocation") + return + } + + func clearCachedLayoutState() { + owsFailDebug("unexpected invocation") + return + } + + func copyMediaAction() { + owsFailDebug("unexpected invocation") + return + } + + func copyTextAction() { + owsFailDebug("unexpected invocation") + return + } + + func shareMediaAction() { + owsFailDebug("unexpected invocation") + return + } + + func shareTextAction() { + owsFailDebug("unexpected invocation") + return + } + + func saveMediaAction() { + owsFailDebug("unexpected invocation") + return + } + + func deleteAction() { + owsFailDebug("unexpected invocation") + return + } + + func canSaveMedia() -> Bool { + owsFailDebug("unexpected invocation") + return false + } + + func audioPlaybackState() -> AudioPlaybackState { + owsFailDebug("unexpected invocation") + return AudioPlaybackState.paused + } + + func setAudioPlaybackState(_ state: AudioPlaybackState) { + owsFailDebug("unexpected invocation") + return + } + + func setAudioProgress(_ progress: CGFloat, duration: CGFloat) { + owsFailDebug("unexpected invocation") + return + } + + func cellSize() -> CGSize { + owsFailDebug("unexpected invocation") + return CGSize.zero + } + + func vSpacing(withPreviousLayoutItem previousLayoutItem: ConversationViewLayoutItem) -> CGFloat { + owsFailDebug("unexpected invocation") + return 2 + } +} + +private class MockIncomingMessage: TSIncomingMessage { + init(messageBody: String) { + super.init(incomingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), + in: TSThread(), + authorId: "+fake-id", + sourceDeviceId: 1, + messageBody: messageBody, + attachmentIds: [], + expiresInSeconds: 0, + quotedMessage: nil, + contactShare: nil) + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + required init(dictionary dictionaryValue: [AnyHashable: Any]!) throws { + fatalError("init(dictionary:) has not been implemented") + } + + override func save(with transaction: YapDatabaseReadWriteTransaction) { + // no - op + owsFailDebug("shouldn't save mock message") + } +} + +private class MockOutgoingMessage: TSOutgoingMessage { + init(messageBody: String) { + super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), + in: nil, + messageBody: messageBody, + attachmentIds: [], + expiresInSeconds: 0, + expireStartedAt: 0, + isVoiceMessage: false, + groupMetaMessage: .unspecified, + quotedMessage: nil, + contactShare: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + required init(dictionary dictionaryValue: [AnyHashable: Any]!) throws { + fatalError("init(dictionary:) has not been implemented") + } + + override func save(with transaction: YapDatabaseReadWriteTransaction) { + // no - op + owsFailDebug("shouldn't save mock message") + } + + class MockOutgoingMessageRecipientState: TSOutgoingMessageRecipientState { + override var state: OWSOutgoingMessageRecipientState { + return OWSOutgoingMessageRecipientState.sent + } + + override var deliveryTimestamp: NSNumber? { + return NSNumber(value: NSDate.ows_millisecondTimeStamp()) + } + + override var readTimestamp: NSNumber? { + return NSNumber(value: NSDate.ows_millisecondTimeStamp()) + } + } + + override func readRecipientIds() -> [String] { + // makes message appear as read + return ["fake-non-empty-id"] + } + + override func recipientState(forRecipientId recipientId: String) -> TSOutgoingMessageRecipientState? { + return MockOutgoingMessageRecipientState() + } +} diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h index 84ad462c10..cc2c1a022c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h @@ -6,7 +6,6 @@ NS_ASSUME_NONNULL_BEGIN @class ConversationStyle; @class ConversationViewCell; -@class ConversationViewItem; @class OWSContactOffersInteraction; @class OWSContactsManager; @class TSAttachmentPointer; @@ -19,13 +18,15 @@ NS_ASSUME_NONNULL_BEGIN @class TSOutgoingMessage; @class TSQuotedMessage; +@protocol ConversationViewItem; + @protocol ConversationViewCellDelegate -- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem; -- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem; -- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(id)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(id)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(id)viewItem; - (void)conversationCell:(ConversationViewCell *)cell - didLongpressSystemMessageViewItem:(ConversationViewItem *)viewItem; + didLongpressSystemMessageViewItem:(id)viewItem; #pragma mark - System Cell @@ -68,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable, weak) id delegate; -@property (nonatomic, nullable) ConversationViewItem *viewItem; +@property (nonatomic, nullable) id viewItem; // Cells are prefetched but expensive cells (e.g. media) should only load // when visible and unload when no longer visible. Non-visible cells can diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h index 4c3eb02a8b..1d0c98e6e2 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.h @@ -7,14 +7,15 @@ NS_ASSUME_NONNULL_BEGIN @class ConversationStyle; -@class ConversationViewItem; @class TSAttachmentStream; +@protocol ConversationViewItem; + @interface OWSAudioMessageView : UIStackView - (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming - viewItem:(ConversationViewItem *)viewItem + viewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle; - (void)createContents; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m index e380c4acd7..f06f9a2ea9 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) TSAttachmentStream *attachmentStream; @property (nonatomic) BOOL isIncoming; -@property (nonatomic, weak) ConversationViewItem *viewItem; +@property (nonatomic, weak) id viewItem; @property (nonatomic, readonly) ConversationStyle *conversationStyle; @property (nonatomic, nullable) UIButton *audioPlayPauseButton; @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithAttachment:(TSAttachmentStream *)attachmentStream isIncoming:(BOOL)isIncoming - viewItem:(ConversationViewItem *)viewItem + viewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle { self = [super init]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h index b899ed1b10..33fa1b9f69 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h @@ -6,7 +6,9 @@ NS_ASSUME_NONNULL_BEGIN @class ContactShareViewModel; @class ConversationStyle; -@class ConversationViewItem; + +@protocol ConversationViewItem; + @class OWSContact; @class OWSQuotedReplyModel; @class TSAttachmentPointer; @@ -25,27 +27,27 @@ extern const UIDataDetectorTypes kOWSAllowedDataDetectorTypes; @protocol OWSMessageBubbleViewDelegate -- (void)didTapImageViewItem:(ConversationViewItem *)viewItem +- (void)didTapImageViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream imageView:(UIView *)imageView; -- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem +- (void)didTapVideoViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream imageView:(UIView *)imageView; -- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; +- (void)didTapAudioViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; -- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem; +- (void)didTapTruncatedTextMessage:(id)conversationItem; -- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem +- (void)didTapFailedIncomingAttachment:(id)viewItem attachmentPointer:(TSAttachmentPointer *)attachmentPointer; -- (void)didTapConversationItem:(ConversationViewItem *)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply; -- (void)didTapConversationItem:(ConversationViewItem *)viewItem +- (void)didTapConversationItem:(id)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply; +- (void)didTapConversationItem:(id)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply failedThumbnailDownloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer; -- (void)didTapContactShareViewItem:(ConversationViewItem *)viewItem; +- (void)didTapContactShareViewItem:(id)viewItem; - (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare NS_SWIFT_NAME(didTapSendMessage(toContactShare:)); @@ -60,7 +62,7 @@ extern const UIDataDetectorTypes kOWSAllowedDataDetectorTypes; @interface OWSMessageBubbleView : UIView -@property (nonatomic, nullable) ConversationViewItem *viewItem; +@property (nonatomic, nullable) id viewItem; @property (nonatomic) ConversationStyle *conversationStyle; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h index 55ef23ff51..000dc87768 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h @@ -2,19 +2,20 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // -@class ConversationStyle; -@class ConversationViewItem; - NS_ASSUME_NONNULL_BEGIN +@class ConversationStyle; + +@protocol ConversationViewItem; + @interface OWSMessageFooterView : UIStackView -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem +- (void)configureWithConversationViewItem:(id)viewItem isOverlayingMedia:(BOOL)isOverlayingMedia conversationStyle:(ConversationStyle *)conversationStyle isIncoming:(BOOL)isIncoming; -- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem; +- (CGSize)measureWithConversationViewItem:(id)viewItem; - (void)prepareForReuse; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m index 253a08b446..69c18e14c1 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m @@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Load -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem +- (void)configureWithConversationViewItem:(id)viewItem isOverlayingMedia:(BOOL)isOverlayingMedia conversationStyle:(ConversationStyle *)conversationStyle isIncoming:(BOOL)isIncoming @@ -186,7 +186,7 @@ NS_ASSUME_NONNULL_BEGIN [self.statusIndicatorImageView.layer addAnimation:animation forKey:@"animation"]; } -- (BOOL)isFailedOutgoingMessage:(ConversationViewItem *)viewItem +- (BOOL)isFailedOutgoingMessage:(id)viewItem { OWSAssertDebug(viewItem); @@ -200,7 +200,7 @@ NS_ASSUME_NONNULL_BEGIN return messageStatus == MessageReceiptStatusFailed; } -- (void)configureLabelsWithConversationViewItem:(ConversationViewItem *)viewItem +- (void)configureLabelsWithConversationViewItem:(id)viewItem { OWSAssertDebug(viewItem); @@ -217,7 +217,7 @@ NS_ASSUME_NONNULL_BEGIN self.timestampLabel.text = timestampLabelText.localizedUppercaseString; } -- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem +- (CGSize)measureWithConversationViewItem:(id)viewItem { OWSAssertDebug(viewItem); @@ -243,7 +243,7 @@ NS_ASSUME_NONNULL_BEGIN return CGSizeCeil(result); } -- (nullable NSString *)messageStatusTextForConversationViewItem:(ConversationViewItem *)viewItem +- (nullable NSString *)messageStatusTextForConversationViewItem:(id)viewItem { OWSAssertDebug(viewItem); if (viewItem.interaction.interactionType != OWSInteractionType_OutgoingMessage) { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.h index 797120d272..2b5cb8e565 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.h @@ -5,16 +5,17 @@ extern const CGFloat OWSMessageHeaderViewDateHeaderVMargin; @class ConversationStyle; -@class ConversationViewItem; + +@protocol ConversationViewItem; NS_ASSUME_NONNULL_BEGIN @interface OWSMessageHeaderView : UIStackView -- (void)loadForDisplayWithViewItem:(ConversationViewItem *)viewItem +- (void)loadForDisplayWithViewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle; -- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem +- (CGSize)measureWithConversationViewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle; @end diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m index e3aa56fb5d..f2a37d0c39 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m @@ -72,7 +72,7 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; [self addSubview:self.stackView]; } -- (void)loadForDisplayWithViewItem:(ConversationViewItem *)viewItem +- (void)loadForDisplayWithViewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle { OWSAssertDebug(viewItem); @@ -100,7 +100,7 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; ]; } -- (CGFloat)strokeThicknessWithViewItem:(ConversationViewItem *)viewItem +- (CGFloat)strokeThicknessWithViewItem:(id)viewItem { OWSAssertDebug(viewItem); @@ -111,7 +111,7 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; } } -- (UIColor *)strokeColorWithViewItem:(ConversationViewItem *)viewItem +- (UIColor *)strokeColorWithViewItem:(id)viewItem { OWSAssertDebug(viewItem); @@ -122,11 +122,11 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; } } -- (void)configureLabelsWithViewItem:(ConversationViewItem *)viewItem +- (void)configureLabelsWithViewItem:(id)viewItem { OWSAssertDebug(viewItem); - NSDate *date = viewItem.interaction.receivedAtDate; + NSDate *date = viewItem.interaction.dateForSorting; NSString *dateString = [DateUtil formatDateForConversationDateBreaks:date].localizedUppercaseString; // Update cell to reflect changes in dynamic text. @@ -158,7 +158,7 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; } } -- (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem +- (CGSize)measureWithConversationViewItem:(id)viewItem conversationStyle:(ConversationStyle *)conversationStyle { OWSAssertDebug(viewItem); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 46ae5757b1..bce53d9978 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -64,6 +64,7 @@ #import #import #import +#import #import #import #import @@ -174,8 +175,8 @@ typedef enum : NSUInteger { @property (nonatomic, readonly) ConversationViewLayout *layout; @property (nonatomic, readonly) ConversationStyle *conversationStyle; -@property (nonatomic) NSArray *viewItems; -@property (nonatomic) NSMutableDictionary *viewItemCache; +@property (nonatomic) NSArray> *viewItems; +@property (nonatomic) NSMutableDictionary> *viewItemCache; @property (nonatomic, nullable) AVAudioRecorder *audioRecorder; @property (nonatomic, nullable) OWSAudioPlayer *audioAttachmentPlayer; @@ -214,7 +215,7 @@ typedef enum : NSUInteger { @property (nonatomic) BOOL hasClearedUnreadMessagesIndicator; @property (nonatomic) BOOL showLoadMoreHeader; @property (nonatomic) UILabel *loadMoreHeader; -@property (nonatomic) uint64_t lastVisibleSortId; +@property (nonatomic) uint64_t lastVisibleTimestamp; @property (nonatomic) BOOL isUserScrolling; @@ -705,7 +706,7 @@ typedef enum : NSUInteger { [self scrollToDefaultPosition]; } - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; if (!self.viewHasEverAppeared) { NSTimeInterval appearenceDuration = CACurrentMediaTime() - self.viewControllerCreatedAt; @@ -716,7 +717,7 @@ typedef enum : NSUInteger { - (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator { NSInteger row = 0; - for (ConversationViewItem *viewItem in self.viewItems) { + for (id viewItem in self.viewItems) { if (viewItem.unreadIndicator) { return [NSIndexPath indexPathForRow:row inSection:0]; } @@ -1552,7 +1553,7 @@ typedef enum : NSUInteger { OWSLogInfo(@"didChangePreferredContentSize"); // Evacuate cached cell sizes. - for (ConversationViewItem *viewItem in self.viewItems) { + for (id viewItem in self.viewItems) { [viewItem clearCachedLayoutState]; } [self resetContentAndLayout]; @@ -1890,11 +1891,7 @@ typedef enum : NSUInteger { // DEPRECATED: we're no longer creating these incoming SN error's per message, // but there will be some legacy ones in the wild, behind which await // as-of-yet-undecrypted messages -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" if ([errorMessage isKindOfClass:[TSInvalidIdentityKeyReceivingErrorMessage class]]) { -#pragma clang diagnostic pop - [errorMessage acceptNewIdentityKey]; } }]; @@ -1936,12 +1933,12 @@ typedef enum : NSUInteger { #pragma mark - MessageActionsDelegate -- (void)messageActionsShowDetailsForItem:(ConversationViewItem *)conversationViewItem +- (void)messageActionsShowDetailsForItem:(id)conversationViewItem { [self showDetailViewForViewItem:conversationViewItem]; } -- (void)messageActionsReplyToItem:(ConversationViewItem *)conversationViewItem +- (void)messageActionsReplyToItem:(id)conversationViewItem { [self populateReplyForViewItem:conversationViewItem]; } @@ -2009,27 +2006,32 @@ typedef enum : NSUInteger { #pragma mark - ConversationViewCellDelegate -- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem +- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(id)viewItem { - NSArray *messageActions = [viewItem mediaActionsWithDelegate:self]; + NSArray *messageActions = + [ConversationViewItemActions mediaActionsWithConversationViewItem:viewItem delegate:self]; [self presentMessageActions:messageActions withFocusedCell:cell]; } -- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem +- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(id)viewItem { - NSArray *messageActions = [viewItem textActionsWithDelegate:self]; + NSArray *messageActions = + [ConversationViewItemActions textActionsWithConversationViewItem:viewItem delegate:self]; [self presentMessageActions:messageActions withFocusedCell:cell]; } -- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem +- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(id)viewItem { - NSArray *messageActions = [viewItem quotedMessageActionsWithDelegate:self]; + NSArray *messageActions = + [ConversationViewItemActions quotedMessageActionsWithConversationViewItem:viewItem delegate:self]; [self presentMessageActions:messageActions withFocusedCell:cell]; } -- (void)conversationCell:(ConversationViewCell *)cell didLongpressSystemMessageViewItem:(ConversationViewItem *)viewItem +- (void)conversationCell:(ConversationViewCell *)cell + didLongpressSystemMessageViewItem:(id)viewItem { - NSArray *messageActions = [viewItem infoMessageActionsWithDelegate:self]; + NSArray *messageActions = + [ConversationViewItemActions infoMessageActionsWithConversationViewItem:viewItem delegate:self]; [self presentMessageActions:messageActions withFocusedCell:cell]; } @@ -2144,7 +2146,7 @@ typedef enum : NSUInteger { #pragma mark - OWSMessageBubbleViewDelegate -- (void)didTapImageViewItem:(ConversationViewItem *)viewItem +- (void)didTapImageViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream imageView:(UIView *)imageView { @@ -2175,7 +2177,7 @@ typedef enum : NSUInteger { [vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView]; } -- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem +- (void)didTapVideoViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream imageView:(UIImageView *)imageView { @@ -2204,7 +2206,7 @@ typedef enum : NSUInteger { [vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView]; } -- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream +- (void)didTapAudioViewItem:(id)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream { OWSAssertIsOnMainThread(); OWSAssertDebug(viewItem); @@ -2234,7 +2236,7 @@ typedef enum : NSUInteger { [self.audioAttachmentPlayer playWithPlaybackAudioCategory]; } -- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem +- (void)didTapTruncatedTextMessage:(id)conversationItem { OWSAssertIsOnMainThread(); OWSAssertDebug(conversationItem); @@ -2244,7 +2246,7 @@ typedef enum : NSUInteger { [self.navigationController pushViewController:view animated:YES]; } -- (void)didTapContactShareViewItem:(ConversationViewItem *)conversationItem +- (void)didTapContactShareViewItem:(id)conversationItem { OWSAssertIsOnMainThread(); OWSAssertDebug(conversationItem); @@ -2279,7 +2281,7 @@ typedef enum : NSUInteger { [self.contactShareViewHelper showAddToContactsWithContactShare:contactShare fromViewController:self]; } -- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem +- (void)didTapFailedIncomingAttachment:(id)viewItem attachmentPointer:(TSAttachmentPointer *)attachmentPointer { OWSAssertIsOnMainThread(); @@ -2299,7 +2301,7 @@ typedef enum : NSUInteger { [self handleUnsentMessageTap:message]; } -- (void)didTapConversationItem:(ConversationViewItem *)viewItem +- (void)didTapConversationItem:(id)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply failedThumbnailDownloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer { @@ -2337,7 +2339,7 @@ typedef enum : NSUInteger { }]; } -- (void)didTapConversationItem:(ConversationViewItem *)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply +- (void)didTapConversationItem:(id)viewItem quotedReply:(OWSQuotedReplyModel *)quotedReply { OWSAssertIsOnMainThread(); OWSAssertDebug(viewItem); @@ -2472,7 +2474,7 @@ typedef enum : NSUInteger { return @(groupIndex); } -- (void)showDetailViewForViewItem:(ConversationViewItem *)conversationItem +- (void)showDetailViewForViewItem:(id)conversationItem { OWSAssertIsOnMainThread(); OWSAssertDebug(conversationItem); @@ -2487,7 +2489,7 @@ typedef enum : NSUInteger { [self.navigationController pushViewController:view animated:YES]; } -- (void)populateReplyForViewItem:(ConversationViewItem *)conversationItem +- (void)populateReplyForViewItem:(id)conversationItem { OWSLogDebug(@"user did tap reply"); @@ -2562,7 +2564,7 @@ typedef enum : NSUInteger { NSIndexPath *_Nullable indexPathOfUnreadIndicator = [self indexPathOfUnreadMessagesIndicator]; if (indexPathOfUnreadIndicator) { - ConversationViewItem *oldIndicatorItem = [self viewItemForIndex:indexPathOfUnreadIndicator.row]; + id oldIndicatorItem = [self viewItemForIndex:indexPathOfUnreadIndicator.row]; OWSAssertDebug(oldIndicatorItem); // TODO ideally this would be happening within the *same* transaction that caused the unreadMessageIndicator @@ -2676,10 +2678,10 @@ typedef enum : NSUInteger { BOOL isScrolledUp = scrollSpaceToBottom > pageHeight * 1.f; if (self.viewItems.count > 0) { - ConversationViewItem *lastViewItem = [self.viewItems lastObject]; + id lastViewItem = [self.viewItems lastObject]; OWSAssertDebug(lastViewItem); - if (lastViewItem.interaction.sortId > self.lastVisibleSortId) { + if (lastViewItem.interaction.timestampForSorting > self.lastVisibleTimestamp) { shouldShowScrollDownButton = YES; } else if (isScrolledUp) { shouldShowScrollDownButton = YES; @@ -2778,6 +2780,7 @@ typedef enum : NSUInteger { OWSAssertIsOnMainThread(); OWSAssertDebug(message); + [self updateLastVisibleTimestamp:message.timestampForSorting]; self.lastMessageSentDate = [NSDate new]; [self clearUnreadMessagesIndicator]; self.inputToolbar.quotedReply = nil; @@ -3291,7 +3294,7 @@ typedef enum : NSUInteger { case YapDatabaseViewChangeUpdate: { YapCollectionKey *collectionKey = rowChange.collectionKey; if (collectionKey.key) { - ConversationViewItem *_Nullable viewItem = self.viewItemCache[collectionKey.key]; + id _Nullable viewItem = self.viewItemCache[collectionKey.key]; if (viewItem) { [self reloadInteractionForViewItem:viewItem]; } else { @@ -3329,7 +3332,7 @@ typedef enum : NSUInteger { // using the more extreme actions in the debug UI. OWSFailDebug(@"hasMalformedRowChange"); [self resetMappings]; - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; [self scrollToBottomAnimated:NO]; return; } @@ -3339,7 +3342,7 @@ typedef enum : NSUInteger { // These errors are rare. OWSFailDebug(@"could not reload view items; hard resetting message mappings."); [self resetMappings]; - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; [self scrollToBottomAnimated:NO]; return; } @@ -3375,7 +3378,8 @@ typedef enum : NSUInteger { (unsigned long)rowChange.finalIndex); [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; + id _Nullable viewItem = + [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction; if (!outgoingMessage.isFromLinkedDevice) { @@ -3417,8 +3421,8 @@ typedef enum : NSUInteger { if (!finished) { OWSLogInfo(@"performBatchUpdates did not finish"); } - - [self updateLastVisibleSortId]; + + [self updateLastVisibleTimestamp]; if (scrollToBottom && shouldAnimateUpdates) { [self scrollToBottomAnimated:shouldAnimateScrollToBottom]; @@ -3510,7 +3514,7 @@ typedef enum : NSUInteger { isOnlyModifyingLastMessage = NO; break; case YapDatabaseViewChangeInsert: { - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; + id _Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] || [viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) && rowChange.finalIndex >= oldViewItemCount) { @@ -3526,7 +3530,7 @@ typedef enum : NSUInteger { if (rowChange.changes == YapDatabaseViewChangedDependency) { continue; } - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; + id _Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if (([viewItem.interaction isKindOfClass:[TSIncomingMessage class]] || [viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) && rowChange.finalIndex >= oldViewItemCount) { @@ -3857,7 +3861,7 @@ typedef enum : NSUInteger { return lastVisibleIndexPath; } -- (nullable ConversationViewItem *)lastVisibleViewItem +- (nullable id)lastVisibleViewItem { NSIndexPath *_Nullable lastVisibleIndexPath = [self lastVisibleIndexPath]; if (!lastVisibleIndexPath) { @@ -3872,10 +3876,10 @@ typedef enum : NSUInteger { - (void)didScrollToBottom { - ConversationViewItem *_Nullable lastVisibleViewItem = [self.viewItems lastObject]; + id _Nullable lastVisibleViewItem = [self.viewItems lastObject]; if (lastVisibleViewItem) { - uint64_t lastVisibleSortId = lastVisibleViewItem.interaction.sortId; - self.lastVisibleSortId = MAX(self.lastVisibleSortId, lastVisibleSortId); + uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting; + self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp); } self.scrollDownButton.hidden = YES; @@ -3883,12 +3887,12 @@ typedef enum : NSUInteger { self.hasUnreadMessages = NO; } -- (void)updateLastVisibleSortId +- (void)updateLastVisibleTimestamp { - ConversationViewItem *_Nullable lastVisibleViewItem = [self lastVisibleViewItem]; + id _Nullable lastVisibleViewItem = [self lastVisibleViewItem]; if (lastVisibleViewItem) { - uint64_t lastVisibleSortId = lastVisibleViewItem.interaction.sortId; - self.lastVisibleSortId = MAX(self.lastVisibleSortId, lastVisibleSortId); + uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting; + self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp); } [self ensureScrollDownButton]; @@ -3901,6 +3905,15 @@ typedef enum : NSUInteger { self.hasUnreadMessages = numberOfUnreadMessages > 0; } +- (void)updateLastVisibleTimestamp:(uint64_t)timestamp +{ + OWSAssertDebug(timestamp > 0); + + self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, timestamp); + + [self ensureScrollDownButton]; +} + - (void)markVisibleMessagesAsRead { if (self.presentedViewController) { @@ -3916,16 +3929,15 @@ typedef enum : NSUInteger { return; } - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; - uint64_t lastVisibleSortId = self.lastVisibleSortId; + uint64_t lastVisibleTimestamp = self.lastVisibleTimestamp; - if (lastVisibleSortId == 0) { + if (lastVisibleTimestamp == 0) { // No visible messages yet. New Thread. return; } - - [OWSReadReceiptManager.sharedManager markAsReadLocallyBeforeSortId:self.lastVisibleSortId thread:self.thread]; + [OWSReadReceiptManager.sharedManager markAsReadLocallyBeforeTimestamp:lastVisibleTimestamp thread:self.thread]; } - (void)updateGroupModelTo:(TSGroupModel *)newGroupModel successCompletion:(void (^_Nullable)(void))successCompletion @@ -4322,7 +4334,7 @@ typedef enum : NSUInteger { - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; [self autoLoadMoreIfNecessary]; } @@ -4634,7 +4646,7 @@ typedef enum : NSUInteger { // any new items inserted while we were not observing. We therefore find the // first item at or after the "view horizon". See the comments below which explain // the "view horizon". - ConversationViewItem *_Nullable lastViewItem = self.viewItems.lastObject; + id _Nullable lastViewItem = self.viewItems.lastObject; BOOL hasAddedNewItems = (lastViewItem && previousLastTimestamp && lastViewItem.interaction.timestamp > previousLastTimestamp.unsignedLongLongValue); @@ -4665,7 +4677,7 @@ typedef enum : NSUInteger { // We'll use this later to update the view to reflect any changes made while // we were not observing the database. See extendRangeToIncludeUnobservedItems // and the logic above. - ConversationViewItem *_Nullable lastViewItem = self.viewItems.lastObject; + id _Nullable lastViewItem = self.viewItems.lastObject; if (lastViewItem) { self.previousLastTimestamp = @(lastViewItem.interaction.timestamp); } else { @@ -4711,7 +4723,7 @@ typedef enum : NSUInteger { NSUInteger mid = (left + right) / 2; OWSAssertDebug(left <= mid); OWSAssertDebug(mid < right); - ConversationViewItem *viewItem = self.viewItems[mid]; + id viewItem = self.viewItems[mid]; if (viewItem.interaction.timestamp >= viewHorizonTimestamp) { right = mid; } else { @@ -4720,7 +4732,7 @@ typedef enum : NSUInteger { } } OWSAssertDebug(left == right); - ConversationViewItem *viewItem = self.viewItems[left]; + id viewItem = self.viewItems[left]; if (viewItem.interaction.timestamp >= viewHorizonTimestamp) { OWSLogInfo(@"firstIndexPathAtViewHorizonTimestamp: %zd / %zd", left, self.viewItems.count); return [NSIndexPath indexPathForRow:(NSInteger) left inSection:0]; @@ -4842,7 +4854,7 @@ typedef enum : NSUInteger { { OWSAssertIsOnMainThread(); - [self updateLastVisibleSortId]; + [self updateLastVisibleTimestamp]; self.conversationStyle.viewWidth = self.collectionView.width; } @@ -4854,8 +4866,8 @@ typedef enum : NSUInteger { // Returns NO on error. - (BOOL)reloadViewItems { - NSMutableArray *viewItems = [NSMutableArray new]; - NSMutableDictionary *viewItemCache = [NSMutableDictionary new]; + NSMutableArray> *viewItems = [NSMutableArray new]; + NSMutableDictionary> *viewItemCache = [NSMutableDictionary new]; NSUInteger count = [self.messageMappings numberOfItemsInSection:0]; BOOL isGroupThread = self.isGroupConversation; @@ -4884,12 +4896,12 @@ typedef enum : NSUInteger { continue; } - ConversationViewItem *_Nullable viewItem = self.viewItemCache[interaction.uniqueId]; + id _Nullable viewItem = self.viewItemCache[interaction.uniqueId]; if (!viewItem) { - viewItem = [[ConversationViewItem alloc] initWithInteraction:interaction - isGroupThread:isGroupThread - transaction:transaction - conversationStyle:self.conversationStyle]; + viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction + isGroupThread:isGroupThread + transaction:transaction + conversationStyle:self.conversationStyle]; } [viewItems addObject:viewItem]; OWSAssertDebug(!viewItemCache[interaction.uniqueId]); @@ -4910,7 +4922,7 @@ typedef enum : NSUInteger { uint64_t collapseCutoffTimestamp = [NSDate ows_millisecondsSince1970ForDate:self.collapseCutoffDate]; BOOL hasPlacedUnreadIndicator = NO; - for (ConversationViewItem *viewItem in viewItems) { + for (id viewItem in viewItems) { BOOL canShowDate = NO; switch (viewItem.interaction.interactionType) { case OWSInteractionType_Unknown: @@ -4926,7 +4938,7 @@ typedef enum : NSUInteger { break; } - uint64_t viewItemTimestamp = viewItem.interaction.receivedAtTimestamp; + uint64_t viewItemTimestamp = viewItem.interaction.timestampForSorting; OWSAssertDebug(viewItemTimestamp > 0); BOOL shouldShowDate = NO; @@ -4958,15 +4970,17 @@ typedef enum : NSUInteger { && !((id)viewItem.interaction).wasRead); if (isItemUnread && !unreadIndicator && !hasPlacedUnreadIndicator && !self.hasClearedUnreadMessagesIndicator) { - unreadIndicator = [[OWSUnreadIndicator alloc] initWithFirstUnseenSortId:viewItem.interaction.sortId - hasMoreUnseenMessages:NO - missingUnseenSafetyNumberChangeCount:0 - unreadIndicatorPosition:0]; + unreadIndicator = + [[OWSUnreadIndicator alloc] initUnreadIndicatorWithTimestamp:viewItem.interaction.timestamp + hasMoreUnseenMessages:NO + missingUnseenSafetyNumberChangeCount:0 + unreadIndicatorPosition:0 + firstUnseenInteractionTimestamp:viewItem.interaction.timestamp]; } // Place the unread indicator onto the first appropriate view item, // if any. - if (unreadIndicator && viewItem.interaction.sortId >= unreadIndicator.firstUnseenSortId) { + if (unreadIndicator && viewItem.interaction.timestampForSorting >= unreadIndicator.timestamp) { viewItem.unreadIndicator = unreadIndicator; unreadIndicator = nil; hasPlacedUnreadIndicator = YES; @@ -4984,9 +4998,9 @@ typedef enum : NSUInteger { // // NOTE: This logic uses the break properties which are set in the previous pass. for (NSUInteger i = 0; i < viewItems.count; i++) { - ConversationViewItem *viewItem = viewItems[i]; - ConversationViewItem *_Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil); - ConversationViewItem *_Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil); + id viewItem = viewItems[i]; + id _Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil); + id _Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil); BOOL shouldShowSenderAvatar = NO; BOOL shouldHideFooter = NO; BOOL isFirstInCluster = YES; @@ -5116,8 +5130,7 @@ typedef enum : NSUInteger { } } - // Avoid animation churn by supressing footer-collapse on messages received recently. - if (viewItem.interaction.receivedAtTimestamp > collapseCutoffTimestamp) { + if (viewItem.interaction.timestampForSorting > collapseCutoffTimestamp) { shouldHideFooter = NO; } @@ -5136,7 +5149,7 @@ typedef enum : NSUInteger { // Whenever an interaction is modified, we need to reload it from the DB // and update the corresponding view item. -- (void)reloadInteractionForViewItem:(ConversationViewItem *)viewItem +- (void)reloadInteractionForViewItem:(id)viewItem { OWSAssertIsOnMainThread(); OWSAssertDebug(viewItem); @@ -5157,7 +5170,7 @@ typedef enum : NSUInteger { }]; } -- (nullable ConversationViewItem *)viewItemForIndex:(NSInteger)index +- (nullable id)viewItemForIndex:(NSInteger)index { if (index < 0 || index >= (NSInteger)self.viewItems.count) { OWSFailDebug(@"Invalid view item index: %lu", (unsigned long)index); @@ -5176,7 +5189,7 @@ typedef enum : NSUInteger { - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:indexPath.row]; + id _Nullable viewItem = [self viewItemForIndex:indexPath.row]; ConversationViewCell *cell = [viewItem dequeueCellForCollectionView:self.collectionView indexPath:indexPath]; if (!cell) { OWSFailDebug(@"Could not dequeue cell."); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index bc8bc9526c..fe2bb42b9d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -43,7 +43,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); // // Critically, this class implements ConversationViewLayoutItem // and does caching of the cell's size. -@interface ConversationViewItem : NSObject +@protocol ConversationViewItem @property (nonatomic, readonly) TSInteraction *interaction; @@ -69,14 +69,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, nullable) OWSUnreadIndicator *unreadIndicator; -@property (nonatomic, readonly) ConversationStyle *conversationStyle; - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithInteraction:(TSInteraction *)interaction - isGroupThread:(BOOL)isGroupThread - transaction:(YapDatabaseReadTransaction *)transaction - conversationStyle:(ConversationStyle *)conversationStyle; - - (ConversationViewCell *)dequeueCellForCollectionView:(UICollectionView *)collectionView indexPath:(NSIndexPath *)indexPath; @@ -89,21 +81,20 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, weak) OWSAudioMessageView *lastAudioMessageView; @property (nonatomic, readonly) CGFloat audioDurationSeconds; - -- (CGFloat)audioProgressSeconds; +@property (nonatomic, readonly) CGFloat audioProgressSeconds; #pragma mark - View State Caching // These methods only apply to text & attachment messages. -- (OWSMessageCellType)messageCellType; -- (nullable DisplayableText *)displayableBodyText; -- (nullable TSAttachmentStream *)attachmentStream; -- (nullable TSAttachmentPointer *)attachmentPointer; -- (CGSize)mediaSize; +@property (nonatomic, readonly) OWSMessageCellType messageCellType; +@property (nonatomic, readonly, nullable) DisplayableText *displayableBodyText; +@property (nonatomic, readonly, nullable) TSAttachmentStream *attachmentStream; +@property (nonatomic, readonly, nullable) TSAttachmentPointer *attachmentPointer; +@property (nonatomic, readonly) CGSize mediaSize; -- (nullable DisplayableText *)displayableQuotedText; -- (nullable NSString *)quotedAttachmentMimetype; -- (nullable NSString *)quotedRecipientId; +@property (nonatomic, readonly, nullable) DisplayableText *displayableQuotedText; +@property (nonatomic, readonly, nullable) NSString *quotedAttachmentMimetype; +@property (nonatomic, readonly, nullable) NSString *quotedRecipientId; // We don't want to try to load the media for this item (if any) // if a load has previously failed. @@ -132,4 +123,15 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @end +@interface ConversationInteractionViewItem + : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithInteraction:(TSInteraction *)interaction + isGroupThread:(BOOL)isGroupThread + transaction:(YapDatabaseReadTransaction *)transaction + conversationStyle:(ConversationStyle *)conversationStyle; + +@end + NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 4aed320f45..55c53fde0c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -46,7 +46,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) #pragma mark - -@interface ConversationViewItem () +@interface ConversationInteractionViewItem () @property (nonatomic, nullable) NSValue *cachedCellSize; @@ -70,12 +70,25 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @property (nonatomic, nullable) NSString *systemMessageText; @property (nonatomic, nullable) TSThread *incomingMessageAuthorThread; @property (nonatomic, nullable) NSString *authorConversationColorName; +@property (nonatomic, nullable) ConversationStyle *conversationStyle; @end #pragma mark - -@implementation ConversationViewItem +@implementation ConversationInteractionViewItem + +@synthesize shouldShowDate = _shouldShowDate; +@synthesize shouldShowSenderAvatar = _shouldShowSenderAvatar; +@synthesize unreadIndicator = _unreadIndicator; +@synthesize didCellMediaFailToLoad = _didCellMediaFailToLoad; +@synthesize interaction = _interaction; +@synthesize isFirstInCluster = _isFirstInCluster; +@synthesize isGroupThread = _isGroupThread; +@synthesize isLastInCluster = _isLastInCluster; +@synthesize lastAudioMessageView = _lastAudioMessageView; +@synthesize senderName = _senderName; +@synthesize shouldHideFooter = _shouldHideFooter; - (instancetype)initWithInteraction:(TSInteraction *)interaction isGroupThread:(BOOL)isGroupThread @@ -293,7 +306,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return measurementCell; } -- (CGFloat)vSpacingWithPreviousLayoutItem:(ConversationViewItem *)previousLayoutItem +- (CGFloat)vSpacingWithPreviousLayoutItem:(id)previousLayoutItem { OWSAssertDebug(previousLayoutItem); diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m b/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m index ac7e8171e1..2675d3df10 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m @@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN NSUInteger index, BOOL *stop) { NSTimeInterval ageSeconds - = fabs(interaction.receivedAtDate.timeIntervalSinceNow); + = fabs(interaction.dateForSorting.timeIntervalSinceNow); if (ageSeconds < maxAgeSeconds) { *stop = YES; return; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index f72e4938f1..b7d1b0a868 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -1967,10 +1967,11 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(messageToQuote); OWSLogVerbose(@"%@", label); [DDLog flushLog]; - ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote - isGroupThread:thread.isGroupThread - transaction:transaction - conversationStyle:conversationStyle]; + id viewItem = + [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote + isGroupThread:thread.isGroupThread + transaction:transaction + conversationStyle:conversationStyle]; quotedMessage = [ [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction] buildQuotedMessageForSending]; @@ -1986,10 +1987,11 @@ NS_ASSUME_NONNULL_BEGIN transaction:transaction]; OWSAssertDebug(messageToQuote); - ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote - isGroupThread:thread.isGroupThread - transaction:transaction - conversationStyle:conversationStyle]; + id viewItem = + [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote + isGroupThread:thread.isGroupThread + transaction:transaction + conversationStyle:conversationStyle]; quotedMessage = [ [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction] buildQuotedMessageForSending]; @@ -3441,7 +3443,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3456,7 +3457,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3471,7 +3471,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; - // MJK TODO - remove senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3484,7 +3483,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:NO durationSeconds:0]; - // MJK TODO - remove senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3495,55 +3493,44 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [result addObject:[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:@"+19174054215"]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeSessionDidEnd]]; // TODO: customMessage? - // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeGroupUpdate]]; // TODO: customMessage? - // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeGroupQuit]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateDefault isLocalChange:YES]]; - - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateVerified isLocalChange:YES]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateNoLongerVerified isLocalChange:YES]]; - - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateDefault isLocalChange:NO]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateVerified isLocalChange:NO]]; - // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3559,17 +3546,13 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac withTransaction:transaction]]; [result addObject:[TSErrorMessage corruptedMessageWithEnvelope:[self createEnvelopeForThread:thread] withTransaction:transaction]]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + + TSInvalidIdentityKeyReceivingErrorMessage *_Nullable blockingSNChangeMessage = [TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:[self createEnvelopeForThread:thread] withTransaction:transaction]; -#pragma clang diagnostic pop - OWSAssertDebug(blockingSNChangeMessage); [result addObject:blockingSNChangeMessage]; - // MJK TODO - should be safe to remove this senderTimestamp [result addObject:[[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageNonBlockingIdentityChange @@ -3732,10 +3715,9 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac OWSLogInfo(@"sendFakeMessages: %lu", (unsigned long)counter); for (NSUInteger i = 0; i < counter; i++) { - NSString *randomText = [[self randomText] stringByAppendingFormat:@" (sequence: %lu)", (unsigned long)i + 1]; + NSString *randomText = [self randomText]; switch (arc4random_uniform(isTextOnly ? 2 : 4)) { case 0: { - // MJK - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -3773,7 +3755,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac attachmentType:TSAttachmentTypeDefault]; pointer.state = TSAttachmentPointerStateFailed; [pointer saveWithTransaction:transaction]; - // MJK - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4172,7 +4153,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:initialTransaction]; - // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4240,7 +4220,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac for (NSNumber *timestamp in timestamps) { NSString *randomText = [self randomText]; { - // Legit usage of SenderTimestamp to backdate incoming sent messages for Debug TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp.unsignedLongLongValue inThread:thread @@ -4254,7 +4233,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [message markAsReadNowWithSendReadReceipt:NO transaction:transaction]; } { - // MJK TODO - this might be the one place we actually use senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:timestamp.unsignedLongLongValue inThread:thread @@ -4281,8 +4259,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac + (void)createDisappearingMessagesWhichFailedToStartInThread:(TSThread *)thread { uint64_t now = [NSDate ows_millisecondTimeStamp]; - - // MJK TODO - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:now inThread:thread @@ -4516,7 +4492,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [attachmentIds addObject:attachmentId]; } - // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4606,7 +4581,6 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac // uint64_t millisAgo = (uint64_t)(((double)arc4random() / ((double)0xffffffff)) * yearsMillis); // uint64_t timestamp = [NSDate ows_millisecondTimeStamp] - millisAgo; - // MJK TODO - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift b/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift index e4e690037b..a57e7bfc77 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift +++ b/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift @@ -41,7 +41,6 @@ class DebugUIProfile: DebugUIPage { OWSTableItem(title: "Send Profile Key Message") { [weak self] in guard let strongSelf = self else { return } - // MJK TODO - should be safe to remove this senderTimestamp let message = OWSProfileKeyMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: aThread) strongSelf.messageSender.sendPromise(message: message).then { Logger.info("Successfully sent profile key message to thread: \(String(describing: aThread))") diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index a811637272..fefa070407 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -251,11 +251,12 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(UIFont.ows_dynamicTypeBodyFont.pointSize != 17 || minMargin == 12); [self.viewConstraints addObjectsFromArray:@[ + // badge sizing [self.unreadBadge autoMatchDimension:ALDimensionWidth toDimension:ALDimensionWidth ofView:self.unreadLabel - withOffset:minMargin], - // badge sizing + withOffset:minMargin + relation:NSLayoutRelationGreaterThanOrEqual], [self.unreadBadge autoSetDimension:ALDimensionWidth toSize:unreadBadgeHeight relation:NSLayoutRelationGreaterThanOrEqual], diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 1356c3a996..7bad90f031 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -496,20 +496,31 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations } // Settings button. - const NSUInteger kAvatarSize = 28; - UIImage *_Nullable localProfileAvatarImage = [OWSProfileManager.sharedManager localProfileAvatarImage]; - UIImage *avatarImage = (localProfileAvatarImage - ?: [[[OWSContactAvatarBuilder alloc] initForLocalUserWithDiameter:kAvatarSize] buildDefaultImage]); - OWSAssertDebug(avatarImage); - - AvatarImageView *avatarView = [[AvatarImageView alloc] initWithImage:avatarImage]; - [avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize]; - [avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize]; - UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithCustomView:avatarView]; - avatarView.userInteractionEnabled = YES; - [avatarView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(settingsButtonPressed:)]]; - settingsButton.accessibilityLabel = CommonStrings.openSettingsButton; + UIBarButtonItem *settingsButton; + if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0)) { + const NSUInteger kAvatarSize = 28; + UIImage *_Nullable localProfileAvatarImage = [OWSProfileManager.sharedManager localProfileAvatarImage]; + UIImage *avatarImage = (localProfileAvatarImage + ?: [[[OWSContactAvatarBuilder alloc] initForLocalUserWithDiameter:kAvatarSize] buildDefaultImage]); + OWSAssertDebug(avatarImage); + AvatarImageView *avatarView = [[AvatarImageView alloc] initWithImage:avatarImage]; + [avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize]; + [avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize]; + avatarView.userInteractionEnabled = YES; + [avatarView + addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(settingsButtonPressed:)]]; + settingsButton = [[UIBarButtonItem alloc] initWithCustomView:avatarView]; + settingsButton.accessibilityLabel = CommonStrings.openSettingsButton; + } else { + // iOS 9 has a bug around layout of custom views in UIBarButtonItem, + // so we just use a simple icon. + UIImage *image = [UIImage imageNamed:@"button_settings_white"]; + settingsButton = [[UIBarButtonItem alloc] initWithImage:image + style:UIBarButtonItemStylePlain + target:self + action:@selector(settingsButtonPressed:)]; + } self.navigationItem.leftBarButtonItem = settingsButton; self.navigationItem.rightBarButtonItem = diff --git a/Signal/src/ViewControllers/LongTextViewController.swift b/Signal/src/ViewControllers/LongTextViewController.swift index 25f6cc6e82..a200bf9fd6 100644 --- a/Signal/src/ViewControllers/LongTextViewController.swift +++ b/Signal/src/ViewControllers/LongTextViewController.swift @@ -37,7 +37,7 @@ public class LongTextViewController: OWSViewController { guard viewItem.hasBodyText else { return "" } - guard let displayableText = viewItem.displayableBodyText() else { + guard let displayableText = viewItem.displayableBodyText else { return "" } let messageBody = displayableText.fullText diff --git a/Signal/src/ViewControllers/MediaDetailViewController.h b/Signal/src/ViewControllers/MediaDetailViewController.h index 5626cb92d4..99e883d803 100644 --- a/Signal/src/ViewControllers/MediaDetailViewController.h +++ b/Signal/src/ViewControllers/MediaDetailViewController.h @@ -6,7 +6,8 @@ NS_ASSUME_NONNULL_BEGIN -@class ConversationViewItem; +@protocol ConversationViewItem; + @class GalleryItemBox; @class MediaDetailViewController; @@ -18,7 +19,7 @@ typedef NS_OPTIONS(NSInteger, MediaGalleryOption) { @protocol MediaDetailViewControllerDelegate - (void)mediaDetailViewController:(MediaDetailViewController *)mediaDetailViewController - requestDeleteConversationViewItem:(ConversationViewItem *)conversationViewItem; + requestDeleteConversationViewItem:(id)conversationViewItem; - (void)mediaDetailViewController:(MediaDetailViewController *)mediaDetailViewController isPlayingVideo:(BOOL)isPlayingVideo; @@ -34,7 +35,7 @@ typedef NS_OPTIONS(NSInteger, MediaGalleryOption) { // If viewItem is non-null, long press will show a menu controller. - (instancetype)initWithGalleryItemBox:(GalleryItemBox *)galleryItemBox - viewItem:(ConversationViewItem *_Nullable)viewItem; + viewItem:(nullable id)viewItem; #pragma mark - Actions - (void)didPressShare:(id)sender; diff --git a/Signal/src/ViewControllers/MediaDetailViewController.m b/Signal/src/ViewControllers/MediaDetailViewController.m index d52ae1e725..4502c0012f 100644 --- a/Signal/src/ViewControllers/MediaDetailViewController.m +++ b/Signal/src/ViewControllers/MediaDetailViewController.m @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIButton *shareButton; @property (nonatomic) TSAttachmentStream *attachmentStream; -@property (nonatomic, nullable) ConversationViewItem *viewItem; +@property (nonatomic, nullable) id viewItem; @property (nonatomic, nullable) UIImage *image; @property (nonatomic, nullable) OWSVideoPlayer *videoPlayer; @@ -63,7 +63,7 @@ NS_ASSUME_NONNULL_BEGIN } - (instancetype)initWithGalleryItemBox:(GalleryItemBox *)galleryItemBox - viewItem:(ConversationViewItem *_Nullable)viewItem + viewItem:(nullable id)viewItem { self = [super initWithNibName:nil bundle:nil]; if (!self) { diff --git a/Signal/src/ViewControllers/MediaGalleryViewController.swift b/Signal/src/ViewControllers/MediaGalleryViewController.swift index 450aef7730..019dc7f092 100644 --- a/Signal/src/ViewControllers/MediaGalleryViewController.swift +++ b/Signal/src/ViewControllers/MediaGalleryViewController.swift @@ -54,7 +54,7 @@ public struct GalleryDate: Hashable, Comparable, Equatable { let month: Int init(message: TSMessage) { - let date = message.receivedAtDate() + let date = message.dateForSorting() self.year = Calendar.current.component(.year, from: date) self.month = Calendar.current.component(.month, from: date) @@ -730,13 +730,13 @@ class MediaGalleryViewController: OWSNavigationController, MediaGalleryDataSourc Bench(title: "sorting gallery items") { galleryItems.sort { lhs, rhs -> Bool in - return lhs.message.sortId < rhs.message.sortId + return lhs.message.timestampForSorting() < rhs.message.timestampForSorting() } sectionDates.sort() for (date, galleryItems) in sections { sortedSections[date] = galleryItems.sorted { lhs, rhs -> Bool in - return lhs.message.sortId < rhs.message.sortId + return lhs.message.timestampForSorting() < rhs.message.timestampForSorting() } } } diff --git a/Signal/src/ViewControllers/MediaPageViewController.swift b/Signal/src/ViewControllers/MediaPageViewController.swift index a12ce8456a..a986070933 100644 --- a/Signal/src/ViewControllers/MediaPageViewController.swift +++ b/Signal/src/ViewControllers/MediaPageViewController.swift @@ -479,10 +479,10 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou let message = galleryItem.message let thread = message.thread(with: transaction) let conversationStyle = ConversationStyle(thread: thread) - fetchedItem = ConversationViewItem(interaction: message, - isGroupThread: thread.isGroupThread(), - transaction: transaction, - conversationStyle: conversationStyle) + fetchedItem = ConversationInteractionViewItem(interaction: message, + isGroupThread: thread.isGroupThread(), + transaction: transaction, + conversationStyle: conversationStyle) } guard let viewItem = fetchedItem else { diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index c739af8a70..8495b353cb 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -294,7 +294,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele if message as? TSIncomingMessage != nil { rows.append(valueRow(name: NSLocalizedString("MESSAGE_METADATA_VIEW_RECEIVED_DATE_TIME", comment: "Label for the 'received date & time' field of the 'message metadata' view."), - value: DateUtil.formatPastTimestampRelativeToNow(message.receivedAtTimestamp))) + value: DateUtil.formatPastTimestampRelativeToNow(message.timestampForSorting()))) } rows += addAttachmentMetadataRows() @@ -326,7 +326,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele guard viewItem.hasBodyText else { return nil } - guard let displayableText = viewItem.displayableBodyText() else { + guard let displayableText = viewItem.displayableBodyText else { return nil } let messageBody = displayableText.fullText @@ -663,7 +663,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele if let audioAttachmentPlayer = self.audioAttachmentPlayer { // Is this player associated with this media adapter? - if audioAttachmentPlayer.owner as? ConversationViewItem == viewItem { + if audioAttachmentPlayer.owner === viewItem { // Tap to pause & unpause. audioAttachmentPlayer.togglePlayState() return diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 78a4a6c2ae..3e988389df 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -464,7 +464,6 @@ NS_ASSUME_NONNULL_BEGIN // Add an error message to the new group indicating // that group creation didn't succeed. - // MJK TODO should be safe to remove senderTimestamp and just save immediately TSErrorMessage *errorMessage = [[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageGroupCreationFailed]; diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 41356ba96f..e55896674f 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -250,9 +250,7 @@ const CGFloat kIconViewLength = 24; [[OWSDisappearingMessagesConfiguration alloc] initDefaultWithThreadId:self.thread.uniqueId]; } - NSString *colorName = self.thread.conversationColorName; - OWSConversationColor *currentConversationColor = [OWSConversationColor conversationColorOrDefaultForColorName:colorName]; - self.colorPicker = [[OWSColorPicker alloc] initWithCurrentConversationColor:currentConversationColor]; + self.colorPicker = [[OWSColorPicker alloc] initWithThread:self.thread]; self.colorPicker.delegate = self; [self updateTableContents]; @@ -909,9 +907,7 @@ const CGFloat kIconViewLength = 24; } if (self.disappearingMessagesConfiguration.dictionaryValueDidChange) { - // TODO - put these two saves in a single transaction [self.disappearingMessagesConfiguration save]; - // MJK TODO - should be safe to remove this senderTimestamp OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 2843428789..ee62a7c160 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -372,7 +372,6 @@ private class SignalCallData: NSObject { let callData = SignalCallData(call: call) self.callData = callData - // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeOutgoingIncomplete, in: call.thread) callRecord.save() call.callRecord = callRecord @@ -528,7 +527,6 @@ private class SignalCallData: NSObject { callRecord.updateCallType(RPRecentCallTypeIncomingMissed) } } else { - // MJK TODO remove this timestamp param call.callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.thread.contactIdentifier(), callType: RPRecentCallTypeIncomingMissed, @@ -615,7 +613,6 @@ private class SignalCallData: NSObject { self.notificationsAdapter.presentMissedCallBecauseOfNoLongerVerifiedIdentity(call: newCall, callerName: callerName) } - // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: thread.contactIdentifier(), callType: RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity, @@ -1038,7 +1035,6 @@ private class SignalCallData: NSObject { Logger.info("\(call.identifiersForLogs).") - // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeIncomingIncomplete, in: call.thread) callRecord.save() call.callRecord = callRecord @@ -1128,7 +1124,6 @@ private class SignalCallData: NSObject { owsFailDebug("Not expecting callrecord to already be set") callRecord.updateCallType(RPRecentCallTypeIncomingDeclined) } else { - // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeIncomingDeclined, in: call.thread) callRecord.save() call.callRecord = callRecord diff --git a/Signal/test/ViewControllers/ConversationViewItemTest.m b/Signal/test/ViewControllers/ConversationViewItemTest.m index d7562718c1..fbb6c718af 100644 --- a/Signal/test/ViewControllers/ConversationViewItemTest.m +++ b/Signal/test/ViewControllers/ConversationViewItemTest.m @@ -39,22 +39,22 @@ return @"abc"; } -- (ConversationViewItem *)textViewItem +- (ConversationInteractionViewItem *)textViewItem { TSOutgoingMessage *message = [TSOutgoingMessage outgoingMessageInThread:self.thread messageBody:self.fakeTextMessageText attachmentId:nil]; [message save]; - __block ConversationViewItem *viewItem = nil; + __block ConversationInteractionViewItem *viewItem = nil; [self readWithBlock:^(YapDatabaseReadTransaction *transaction) { - viewItem = [[ConversationViewItem alloc] initWithInteraction:message - isGroupThread:NO - transaction:transaction - conversationStyle:self.conversationStyle]; + viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:message + isGroupThread:NO + transaction:transaction + conversationStyle:self.conversationStyle]; }]; return viewItem; } -- (ConversationViewItem *)viewItemWithAttachmentMimetype:(NSString *)mimeType filename:(NSString *)filename +- (ConversationInteractionViewItem *)viewItemWithAttachmentMimetype:(NSString *)mimeType filename:(NSString *)filename { OWSAssertDebug(filename.length > 0); @@ -74,33 +74,33 @@ [TSOutgoingMessage outgoingMessageInThread:self.thread messageBody:nil attachmentId:attachment.uniqueId]; [message save]; - __block ConversationViewItem *viewItem = nil; + __block ConversationInteractionViewItem *viewItem = nil; [self readWithBlock:^(YapDatabaseReadTransaction *transaction) { - viewItem = [[ConversationViewItem alloc] initWithInteraction:message - isGroupThread:NO - transaction:transaction - conversationStyle:self.conversationStyle]; + viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:message + isGroupThread:NO + transaction:transaction + conversationStyle:self.conversationStyle]; }]; return viewItem; } -- (ConversationViewItem *)stillImageViewItem +- (ConversationInteractionViewItem *)stillImageViewItem { return [self viewItemWithAttachmentMimetype:OWSMimeTypeImageJpeg filename:@"test-jpg.jpg"]; } -- (ConversationViewItem *)animatedImageViewItem +- (ConversationInteractionViewItem *)animatedImageViewItem { return [self viewItemWithAttachmentMimetype:OWSMimeTypeImageGif filename:@"test-gif.gif"]; } -- (ConversationViewItem *)videoViewItem +- (ConversationInteractionViewItem *)videoViewItem { return [self viewItemWithAttachmentMimetype:@"video/mp4" filename:@"test-mp4.mp4"]; } -- (ConversationViewItem *)audioViewItem +- (ConversationInteractionViewItem *)audioViewItem { return [self viewItemWithAttachmentMimetype:@"audio/mp3" filename:@"test-mp3.mp3"]; } @@ -109,7 +109,7 @@ - (void)testPerformDeleteEditingActionWithNonMediaMessage { - ConversationViewItem *viewItem = self.textViewItem; + ConversationInteractionViewItem *viewItem = self.textViewItem; XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:viewItem.interaction.uniqueId]); [viewItem deleteAction]; @@ -118,7 +118,7 @@ - (void)testPerformDeleteActionWithPhotoMessage { - ConversationViewItem *viewItem = self.stillImageViewItem; + ConversationInteractionViewItem *viewItem = self.stillImageViewItem; XCTAssertEqual((NSUInteger)1, ((TSMessage *)viewItem.interaction).attachmentIds.count); NSString *_Nullable attachmentId = ((TSMessage *)viewItem.interaction).attachmentIds.firstObject; @@ -140,7 +140,7 @@ - (void)testPerformDeleteEditingActionWithAnimatedMessage { - ConversationViewItem *viewItem = self.animatedImageViewItem; + ConversationInteractionViewItem *viewItem = self.animatedImageViewItem; XCTAssertEqual((NSUInteger)1, ((TSMessage *)viewItem.interaction).attachmentIds.count); NSString *_Nullable attachmentId = ((TSMessage *)viewItem.interaction).attachmentIds.firstObject; @@ -162,7 +162,7 @@ - (void)testPerformDeleteEditingActionWithVideoMessage { - ConversationViewItem *viewItem = self.videoViewItem; + ConversationInteractionViewItem *viewItem = self.videoViewItem; XCTAssertEqual((NSUInteger)1, ((TSMessage *)viewItem.interaction).attachmentIds.count); NSString *_Nullable attachmentId = ((TSMessage *)viewItem.interaction).attachmentIds.firstObject; @@ -184,7 +184,7 @@ - (void)testPerformDeleteEditingActionWithAudioMessage { - ConversationViewItem *viewItem = self.audioViewItem; + ConversationInteractionViewItem *viewItem = self.audioViewItem; XCTAssertEqual((NSUInteger)1, ((TSMessage *)viewItem.interaction).attachmentIds.count); NSString *_Nullable attachmentId = ((TSMessage *)viewItem.interaction).attachmentIds.firstObject; @@ -212,7 +212,7 @@ UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil(UIPasteboard.generalPasteboard.string); - ConversationViewItem *viewItem = self.textViewItem; + ConversationInteractionViewItem *viewItem = self.textViewItem; [viewItem copyTextAction]; XCTAssertEqualObjects(self.fakeTextMessageText, UIPasteboard.generalPasteboard.string); } @@ -224,7 +224,7 @@ XCTAssertNil(UIPasteboard.generalPasteboard.image); XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeJPEG]); - ConversationViewItem *viewItem = self.stillImageViewItem; + ConversationInteractionViewItem *viewItem = self.stillImageViewItem; [viewItem copyMediaAction]; NSData *_Nullable copiedData = [UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeJPEG]; XCTAssertTrue(copiedData.length > 0); @@ -237,7 +237,7 @@ XCTAssertNil(UIPasteboard.generalPasteboard.image); XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeGIF]); - ConversationViewItem *viewItem = self.animatedImageViewItem; + ConversationInteractionViewItem *viewItem = self.animatedImageViewItem; [viewItem copyMediaAction]; NSData *_Nullable copiedData = [UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeGIF]; XCTAssertTrue(copiedData.length > 0); @@ -249,7 +249,7 @@ UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMPEG4]); - ConversationViewItem *viewItem = self.videoViewItem; + ConversationInteractionViewItem *viewItem = self.videoViewItem; [viewItem copyMediaAction]; NSData *_Nullable copiedData = [UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMPEG4]; XCTAssertTrue(copiedData.length > 0); @@ -261,7 +261,7 @@ UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMP3]); - ConversationViewItem *viewItem = self.audioViewItem; + ConversationInteractionViewItem *viewItem = self.audioViewItem; [viewItem copyMediaAction]; NSData *_Nullable copiedData = [UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMP3]; XCTAssertTrue(copiedData.length > 0); diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0abea05b81..63bb79e2c5 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -386,6 +386,12 @@ /* Error indicating that the app was prevented from accessing the user's CloudKit account. */ "CLOUDKIT_STATUS_RESTRICTED" = "Signal was not allowed to access your iCloud account for backups."; +/* The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble. */ +"COLOR_PICKER_DEMO_MESSAGE_1" = "Choose the color of outgoing messages in this conversation."; + +/* The second of two messages demonstrating the chosen conversation color, by rendering this message in an incoming message bubble. */ +"COLOR_PICKER_DEMO_MESSAGE_2" = "Only you will see the color you choose."; + /* Modal Sheet title when picking a conversation color. */ "COLOR_PICKER_SHEET_TITLE" = "Conversation Color"; diff --git a/SignalMessaging/Models/OWSContactOffersInteraction.h b/SignalMessaging/Models/OWSContactOffersInteraction.h index 823223960b..2f38781231 100644 --- a/SignalMessaging/Models/OWSContactOffersInteraction.h +++ b/SignalMessaging/Models/OWSContactOffersInteraction.h @@ -4,8 +4,6 @@ #import -@class YapDatabaseReadWriteTransaction; - NS_ASSUME_NONNULL_BEGIN @interface OWSContactOffersInteraction : TSInteraction @@ -13,16 +11,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL hasBlockOffer; @property (nonatomic, readonly) BOOL hasAddToContactsOffer; @property (nonatomic, readonly) BOOL hasAddToProfileWhitelistOffer; - -// TODO - remove this recipientId param -// it's redundant with the interaction's TSContactThread @property (nonatomic, readonly) NSString *recipientId; - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; -// MJK TODO should be safe to remove this timestamp param - (instancetype)initContactOffersWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread hasBlockOffer:(BOOL)hasBlockOffer @@ -30,11 +24,6 @@ NS_ASSUME_NONNULL_BEGIN hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer recipientId:(NSString *)recipientId NS_DESIGNATED_INITIALIZER; -- (void)updateHasBlockOffer:(BOOL)hasBlockOffer - hasAddToContactsOffer:(BOOL)hasAddToContactsOffer - hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer - transaction:(YapDatabaseReadWriteTransaction *)transaction; - @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/Models/OWSContactOffersInteraction.m b/SignalMessaging/Models/OWSContactOffersInteraction.m index 7fa484f19d..6be86f2e1c 100644 --- a/SignalMessaging/Models/OWSContactOffersInteraction.m +++ b/SignalMessaging/Models/OWSContactOffersInteraction.m @@ -6,14 +6,6 @@ NS_ASSUME_NONNULL_BEGIN -@interface OWSContactOffersInteraction () - -@property (nonatomic) BOOL hasBlockOffer; -@property (nonatomic) BOOL hasAddToContactsOffer; -@property (nonatomic) BOOL hasAddToProfileWhitelistOffer; - -@end - @implementation OWSContactOffersInteraction - (instancetype)initWithCoder:(NSCoder *)coder @@ -60,18 +52,6 @@ NS_ASSUME_NONNULL_BEGIN return OWSInteractionType_Offer; } -- (void)updateHasBlockOffer:(BOOL)hasBlockOffer - hasAddToContactsOffer:(BOOL)hasAddToContactsOffer - hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - [self applyChangeToSelfAndLatestCopy:transaction - changeBlock:^(OWSContactOffersInteraction *offers) { - offers.hasBlockOffer = hasBlockOffer; - offers.hasAddToContactsOffer = hasAddToContactsOffer; - offers.hasAddToProfileWhitelistOffer = hasAddToProfileWhitelistOffer; - }]; -} @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/Models/TSUnreadIndicatorInteraction.h b/SignalMessaging/Models/TSUnreadIndicatorInteraction.h index 13a517c147..e1972b7835 100644 --- a/SignalMessaging/Models/TSUnreadIndicatorInteraction.h +++ b/SignalMessaging/Models/TSUnreadIndicatorInteraction.h @@ -7,7 +7,6 @@ NS_ASSUME_NONNULL_BEGIN // This class is vestigial. -__attribute__((deprecated)) @interface TSUnreadIndicatorInteraction : TSInteraction - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; diff --git a/SignalMessaging/Models/TSUnreadIndicatorInteraction.m b/SignalMessaging/Models/TSUnreadIndicatorInteraction.m index e9d96b4c9d..afd976ef04 100644 --- a/SignalMessaging/Models/TSUnreadIndicatorInteraction.m +++ b/SignalMessaging/Models/TSUnreadIndicatorInteraction.m @@ -6,10 +6,7 @@ NS_ASSUME_NONNULL_BEGIN -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation TSUnreadIndicatorInteraction -#pragma clang diagnostic pop - (instancetype)initWithCoder:(NSCoder *)coder { diff --git a/SignalMessaging/SignalMessaging.h b/SignalMessaging/SignalMessaging.h index 5019d5097a..119718eb5b 100644 --- a/SignalMessaging/SignalMessaging.h +++ b/SignalMessaging/SignalMessaging.h @@ -50,6 +50,7 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[]; #import #import #import +#import #import #import #import diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h index 156b45d7a7..ae8e869598 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h @@ -6,7 +6,8 @@ NS_ASSUME_NONNULL_BEGIN -@class ConversationViewItem; +@protocol ConversationViewItem; + @class TSAttachmentPointer; @class TSAttachmentStream; @class TSMessage; @@ -42,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN transaction:(YapDatabaseReadTransaction *)transaction; // Builds a not-yet-sent QuotedReplyModel -+ (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(ConversationViewItem *)conversationItem ++ (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id)conversationItem transaction:(YapDatabaseReadTransaction *)transaction; - (TSQuotedMessage *)buildQuotedMessageForSending; diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m index 394b209484..b7be0720e8 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m @@ -115,7 +115,7 @@ NS_ASSUME_NONNULL_BEGIN thumbnailDownloadFailed:thumbnailDownloadFailed]; } -+ (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(ConversationViewItem *)conversationItem ++ (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id)conversationItem transaction:(YapDatabaseReadTransaction *)transaction; { OWSAssertDebug(conversationItem); @@ -241,7 +241,6 @@ NS_ASSUME_NONNULL_BEGIN { NSArray *attachments = self.attachmentStream ? @[ self.attachmentStream ] : @[]; - // Legit usage of senderTimestamp to reference existing message return [[TSQuotedMessage alloc] initWithTimestamp:self.timestamp authorId:self.authorId body:self.body diff --git a/SignalMessaging/ViewModels/ThreadViewModel.swift b/SignalMessaging/ViewModels/ThreadViewModel.swift index 16f8fa16ae..36cbcd0df8 100644 --- a/SignalMessaging/ViewModels/ThreadViewModel.swift +++ b/SignalMessaging/ViewModels/ThreadViewModel.swift @@ -25,14 +25,12 @@ public class ThreadViewModel: NSObject { @objc public init(thread: TSThread, transaction: YapDatabaseReadTransaction) { self.threadRecord = thread - + self.lastMessageDate = thread.lastMessageDate() self.isGroupThread = thread.isGroupThread() self.name = thread.name() self.isMuted = thread.isMuted self.lastMessageText = thread.lastMessageText(transaction: transaction) - let lastInteraction = thread.lastInteractionForInbox(transaction: transaction) - self.lastMessageForInbox = lastInteraction - self.lastMessageDate = lastInteraction?.receivedAtDate() ?? thread.creationDate + self.lastMessageForInbox = thread.lastInteractionForInbox(transaction: transaction) if let contactThread = thread as? TSContactThread { self.contactIdentifier = contactThread.contactIdentifier() diff --git a/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift b/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift deleted file mode 100644 index ef35b56652..0000000000 --- a/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -class OWS110SortIdMigration: OWSDatabaseMigration { - // increment a similar constant for each migration. - @objc - class func migrationId() -> String { - return "110" - } - - override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) { - Logger.debug("") - BenchAsync(title: "Sort Migration") { completeBenchmark in - self.doMigration { - completeBenchmark() - completion() - } - } - } - - private func doMigration(completion: @escaping OWSDatabaseMigrationCompletion) { - // TODO batch this? - self.dbReadWriteConnection().readWrite { transaction in - - var archivedThreads: [TSThread] = [] - - // get archived threads before migration - TSThread.enumerateCollectionObjects(with: transaction) { (object, _) in - guard let thread = object as? TSThread else { - owsFailDebug("unexpected object: \(type(of: object))") - return - } - - if thread.isArchivedByLegacyTimestampForSorting { - archivedThreads.append(thread) - } - } - - guard let legacySorting: YapDatabaseAutoViewTransaction = transaction.extension(TSMessageDatabaseViewExtensionName_Legacy) as? YapDatabaseAutoViewTransaction else { - owsFailDebug("legacySorting was unexpectedly nil") - return - } - - let totalCount: UInt = legacySorting.numberOfItemsInAllGroups() - var completedCount: UInt = 0 - legacySorting.enumerateGroups { group, _ in - autoreleasepool { - legacySorting.enumerateKeysAndObjects(inGroup: group) { (_, _, object, _, _) in - autoreleasepool { - guard let interaction = object as? TSInteraction else { - owsFailDebug("unexpected object: \(type(of: object))") - return - } - - interaction.saveNextSortId(transaction: transaction) - - completedCount += 1 - - if completedCount % 100 == 0 { - // Legit usage of legacy sorting for migration to new sorting - Logger.info("thread: \(interaction.uniqueThreadId), timestampForLegacySorting:\(interaction.timestampForLegacySorting()), sortId: \(interaction.sortId) totalCount: \(totalCount), completedcount: \(completedCount)") - } - } - } - } - } - - Logger.info("re-archiving \(archivedThreads.count) threads which were previously archived") - for archivedThread in archivedThreads { - archivedThread.archiveThread(with: transaction) - } - - self.save(with: transaction) - } - - completion() - } - -} diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m index 00eb09346d..f150e17b93 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m @@ -44,8 +44,7 @@ NS_ASSUME_NONNULL_BEGIN [[OWS106EnsureProfileComplete alloc] initWithPrimaryStorage:primaryStorage], [[OWS107LegacySounds alloc] initWithPrimaryStorage:primaryStorage], [[OWS108CallLoggingPreference alloc] initWithPrimaryStorage:primaryStorage], - [[OWS109OutgoingMessageState alloc] initWithPrimaryStorage:primaryStorage], - [[OWS110SortIdMigration alloc] initWithPrimaryStorage:primaryStorage] + [[OWS109OutgoingMessageState alloc] initWithPrimaryStorage:primaryStorage] ]; } diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 5f15ee34a8..3932270749 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -1124,7 +1124,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; { OWSAssertIsOnMainThread(); - // MJK TODO - should be safe to remove this senderTimestamp OWSProfileKeyMessage *message = [[OWSProfileKeyMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread]; diff --git a/SignalMessaging/utils/Bench.swift b/SignalMessaging/utils/Bench.swift index 06829833a7..e11e4622bf 100644 --- a/SignalMessaging/utils/Bench.swift +++ b/SignalMessaging/utils/Bench.swift @@ -4,16 +4,7 @@ import Foundation -/// Benchmark async code by calling the passed in block parameter when the work -/// is done. -/// -/// BenchAsync(title: "my benchmark") { completeBenchmark in -/// foo { -/// completeBenchmark() -/// fooCompletion() -/// } -/// } -public func BenchAsync(title: String, block: (@escaping () -> Void) -> Void) { +public func BenchAsync(title: String, block: (() -> Void) -> Void) { let startTime = CFAbsoluteTimeGetCurrent() block { diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index 8f7d5a9d35..8669715c93 100644 --- a/SignalMessaging/utils/ConversationSearcher.swift +++ b/SignalMessaging/utils/ConversationSearcher.swift @@ -5,21 +5,7 @@ import Foundation import SignalServiceKit -public typealias MessageSortKey = UInt64 -public struct ConversationSortKey: Comparable { - let creationDate: Date - let lastMessageReceivedAtDate: Date? - - // MARK: Comparable - - public static func < (lhs: ConversationSortKey, rhs: ConversationSortKey) -> Bool { - let lhsDate = lhs.lastMessageReceivedAtDate ?? lhs.creationDate - let rhsDate = rhs.lastMessageReceivedAtDate ?? rhs.creationDate - return lhsDate < rhsDate - } -} - -public class ConversationSearchResult: Comparable where SortKey: Comparable { +public class ConversationSearchResult: Comparable { public let thread: ThreadViewModel public let messageId: String? @@ -27,9 +13,9 @@ public class ConversationSearchResult: Comparable where SortKey: Compar public let snippet: String? - private let sortKey: SortKey + private let sortKey: UInt64 - init(thread: ThreadViewModel, sortKey: SortKey, messageId: String? = nil, messageDate: Date? = nil, snippet: String? = nil) { + init(thread: ThreadViewModel, sortKey: UInt64, messageId: String? = nil, messageDate: Date? = nil, snippet: String? = nil) { self.thread = thread self.sortKey = sortKey self.messageId = messageId @@ -79,11 +65,11 @@ public class ContactSearchResult: Comparable { public class SearchResultSet { public let searchText: String - public let conversations: [ConversationSearchResult] + public let conversations: [ConversationSearchResult] public let contacts: [ContactSearchResult] - public let messages: [ConversationSearchResult] + public let messages: [ConversationSearchResult] - public init(searchText: String, conversations: [ConversationSearchResult], contacts: [ContactSearchResult], messages: [ConversationSearchResult]) { + public init(searchText: String, conversations: [ConversationSearchResult], contacts: [ContactSearchResult], messages: [ConversationSearchResult]) { self.searchText = searchText self.conversations = conversations self.contacts = contacts @@ -115,9 +101,9 @@ public class ConversationSearcher: NSObject { transaction: YapDatabaseReadTransaction, contactsManager: ContactsManagerProtocol) -> SearchResultSet { - var conversations: [ConversationSearchResult] = [] + var conversations: [ConversationSearchResult] = [] var contacts: [ContactSearchResult] = [] - var messages: [ConversationSearchResult] = [] + var messages: [ConversationSearchResult] = [] var existingConversationRecipientIds: Set = Set() @@ -125,8 +111,7 @@ public class ConversationSearcher: NSObject { if let thread = match as? TSThread { let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let sortKey = ConversationSortKey(creationDate: thread.creationDate, - lastMessageReceivedAtDate: thread.lastInteractionForInbox(transaction: transaction)?.receivedAtDate()) + let sortKey = NSDate.ows_millisecondsSince1970(for: threadViewModel.lastMessageDate) let searchResult = ConversationSearchResult(thread: threadViewModel, sortKey: sortKey) if let contactThread = thread as? TSContactThread { @@ -139,7 +124,7 @@ public class ConversationSearcher: NSObject { let thread = message.thread(with: transaction) let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let sortKey = message.sortId + let sortKey = message.timestamp let searchResult = ConversationSearchResult(thread: threadViewModel, sortKey: sortKey, messageId: message.uniqueId, diff --git a/SignalMessaging/utils/OWSUnreadIndicator.h b/SignalMessaging/utils/OWSUnreadIndicator.h index ee6f7b37ea..b2d5ba9c07 100644 --- a/SignalMessaging/utils/OWSUnreadIndicator.h +++ b/SignalMessaging/utils/OWSUnreadIndicator.h @@ -6,11 +6,13 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSUnreadIndicator : NSObject +@property (nonatomic, readonly) uint64_t timestamp; + @property (nonatomic, readonly) BOOL hasMoreUnseenMessages; @property (nonatomic, readonly) NSUInteger missingUnseenSafetyNumberChangeCount; -// The sortId of the oldest unseen message. +// The timestamp of the oldest unseen message. // // Once we enter messages view, we mark all messages read, so we need // a snapshot of what the first unread message was when we entered the @@ -18,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN // repeatedly. The unread indicator should continue to show up until // it has been cleared, at which point hideUnreadMessagesIndicator is // YES in ensureDynamicInteractionsForThread:... -@property (nonatomic, readonly) uint64_t firstUnseenSortId; +@property (nonatomic, readonly) uint64_t firstUnseenInteractionTimestamp; // The index of the unseen indicator, counting from the _end_ of the conversation // history. @@ -30,10 +32,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithFirstUnseenSortId:(uint64_t)firstUnseenSortId - hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition NS_DESIGNATED_INITIALIZER; +- (instancetype)initUnreadIndicatorWithTimestamp:(uint64_t)timestamp + hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition + firstUnseenInteractionTimestamp:(uint64_t)firstUnseenInteractionTimestamp NS_DESIGNATED_INITIALIZER; @end diff --git a/SignalMessaging/utils/OWSUnreadIndicator.m b/SignalMessaging/utils/OWSUnreadIndicator.m index 3cc384df3f..a621902451 100644 --- a/SignalMessaging/utils/OWSUnreadIndicator.m +++ b/SignalMessaging/utils/OWSUnreadIndicator.m @@ -8,10 +8,11 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSUnreadIndicator -- (instancetype)initWithFirstUnseenSortId:(uint64_t)firstUnseenSortId - hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition +- (instancetype)initUnreadIndicatorWithTimestamp:(uint64_t)timestamp + hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition + firstUnseenInteractionTimestamp:(uint64_t)firstUnseenInteractionTimestamp { self = [super init]; @@ -19,10 +20,11 @@ NS_ASSUME_NONNULL_BEGIN return self; } - _firstUnseenSortId = firstUnseenSortId; + _timestamp = timestamp; _hasMoreUnseenMessages = hasMoreUnseenMessages; _missingUnseenSafetyNumberChangeCount = missingUnseenSafetyNumberChangeCount; _unreadIndicatorPosition = unreadIndicatorPosition; + _firstUnseenInteractionTimestamp = firstUnseenInteractionTimestamp; return self; } @@ -38,8 +40,7 @@ NS_ASSUME_NONNULL_BEGIN } OWSUnreadIndicator *other = object; - return (self.firstUnseenSortId == other.firstUnseenSortId - && self.hasMoreUnseenMessages == other.hasMoreUnseenMessages + return (self.timestamp == other.timestamp && self.hasMoreUnseenMessages == other.hasMoreUnseenMessages && self.missingUnseenSafetyNumberChangeCount == other.missingUnseenSafetyNumberChangeCount && self.unreadIndicatorPosition == other.unreadIndicatorPosition); } diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 8835a87855..b5198df145 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -128,7 +128,6 @@ NS_ASSUME_NONNULL_BEGIN [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); - // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -180,7 +179,6 @@ NS_ASSUME_NONNULL_BEGIN [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); - // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -305,31 +303,20 @@ NS_ASSUME_NONNULL_BEGIN enumerateRowsInGroup:thread.uniqueId usingBlock:^( NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if ([object isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) { -#pragma clang diagnostic pop // Delete this legacy interactions, which has been superseded by // the OWSContactOffersInteraction. [interactionsToDelete addObject:object]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" } else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) { -#pragma clang diagnostic pop // Delete this legacy interactions, which has been superseded by // the OWSContactOffersInteraction. [interactionsToDelete addObject:object]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" } else if ([object isKindOfClass:[OWSAddToProfileWhitelistOfferMessage class]]) { -#pragma clang diagnostic pop // Delete this legacy interactions, which has been superseded by // the OWSContactOffersInteraction. [interactionsToDelete addObject:object]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" } else if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) { -#pragma clang diagnostic pop // Remove obsolete unread indicator interactions; [interactionsToDelete addObject:object]; } else if ([object isKindOfClass:[OWSContactOffersInteraction class]]) { @@ -363,14 +350,14 @@ NS_ASSUME_NONNULL_BEGIN // have been marked as read. // // IFF this variable is non-null, there are unseen messages in the thread. - NSNumber *_Nullable firstUnseenSortId = nil; + NSNumber *_Nullable firstUnseenInteractionTimestamp = nil; if (lastUnreadIndicator) { - firstUnseenSortId = @(lastUnreadIndicator.firstUnseenSortId); + firstUnseenInteractionTimestamp = @(lastUnreadIndicator.firstUnseenInteractionTimestamp); } else { TSInteraction *_Nullable firstUnseenInteraction = [[TSDatabaseView unseenDatabaseViewExtension:transaction] firstObjectInGroup:thread.uniqueId]; if (firstUnseenInteraction) { - firstUnseenSortId = @(firstUnseenInteraction.sortId); + firstUnseenInteractionTimestamp = @(firstUnseenInteraction.timestampForSorting); } } @@ -481,51 +468,59 @@ NS_ASSUME_NONNULL_BEGIN } } - if (existingContactOffers && !shouldHaveContactOffers) { - OWSLogInfo( - @"Removing contact offers: %@ (%llu)", existingContactOffers.uniqueId, existingContactOffers.sortId); - [existingContactOffers removeWithTransaction:transaction]; - } else if (shouldHaveContactOffers) { - if (existingContactOffers) { - // If the contact offers' properties have changed, update them - if (existingContactOffers.hasBlockOffer != shouldHaveBlockOffer - || existingContactOffers.hasAddToContactsOffer != shouldHaveAddToContactsOffer - || existingContactOffers.hasAddToProfileWhitelistOffer != shouldHaveAddToProfileWhitelistOffer) { - OWSLogInfo(@"Updating stale contact offers: %@ (%llu)", - existingContactOffers.uniqueId, - existingContactOffers.sortId); + // We want the offers to be the first interactions in their + // conversation's timeline, so we back-date them to slightly before + // the first message - or at an aribtrary old timestamp if the + // conversation has no messages. + uint64_t contactOffersTimestamp = [NSDate ows_millisecondTimeStamp]; - [existingContactOffers updateHasBlockOffer:shouldHaveBlockOffer - hasAddToContactsOffer:shouldHaveAddToContactsOffer - hasAddToProfileWhitelistOffer:shouldHaveAddToProfileWhitelistOffer - transaction:transaction]; - } - } else { - NSString *recipientId = ((TSContactThread *)thread).contactIdentifier; - - // TODO MJK - remove this timestamp - TSInteraction *offersMessage = [[OWSContactOffersInteraction alloc] - initContactOffersWithTimestamp:[NSDate ows_millisecondTimeStamp] - thread:thread - hasBlockOffer:shouldHaveBlockOffer - hasAddToContactsOffer:shouldHaveAddToContactsOffer - hasAddToProfileWhitelistOffer:shouldHaveAddToProfileWhitelistOffer - recipientId:recipientId]; - [offersMessage saveWithTransaction:transaction]; - - OWSLogInfo(@"Creating contact offers: %@ (%llu)", offersMessage.uniqueId, offersMessage.sortId); + // If the contact offers' properties have changed, discard the current + // one and create a new one. + if (existingContactOffers) { + if (existingContactOffers.hasBlockOffer != shouldHaveBlockOffer + || existingContactOffers.hasAddToContactsOffer != shouldHaveAddToContactsOffer + || existingContactOffers.hasAddToProfileWhitelistOffer != shouldHaveAddToProfileWhitelistOffer) { + OWSLogInfo(@"Removing stale contact offers: %@ (%llu)", + existingContactOffers.uniqueId, + existingContactOffers.timestampForSorting); + // Preserve the timestamp of the existing "contact offers" so that + // we replace it in the same position in the timeline. + contactOffersTimestamp = existingContactOffers.timestamp; + [existingContactOffers removeWithTransaction:transaction]; + existingContactOffers = nil; } } + if (existingContactOffers && !shouldHaveContactOffers) { + OWSLogInfo(@"Removing contact offers: %@ (%llu)", + existingContactOffers.uniqueId, + existingContactOffers.timestampForSorting); + [existingContactOffers removeWithTransaction:transaction]; + } else if (!existingContactOffers && shouldHaveContactOffers) { + NSString *recipientId = ((TSContactThread *)thread).contactIdentifier; + + TSInteraction *offersMessage = + [[OWSContactOffersInteraction alloc] initContactOffersWithTimestamp:contactOffersTimestamp + thread:thread + hasBlockOffer:shouldHaveBlockOffer + hasAddToContactsOffer:shouldHaveAddToContactsOffer + hasAddToProfileWhitelistOffer:shouldHaveAddToProfileWhitelistOffer + recipientId:recipientId]; + [offersMessage saveWithTransaction:transaction]; + + OWSLogInfo( + @"Creating contact offers: %@ (%llu)", offersMessage.uniqueId, offersMessage.timestampForSorting); + } + [self ensureUnreadIndicator:result - thread:thread - transaction:transaction - shouldHaveContactOffers:shouldHaveContactOffers - maxRangeSize:maxRangeSize - blockingSafetyNumberChanges:blockingSafetyNumberChanges - nonBlockingSafetyNumberChanges:nonBlockingSafetyNumberChanges - hideUnreadMessagesIndicator:hideUnreadMessagesIndicator - firstUnseenSortId:firstUnseenSortId]; + thread:thread + transaction:transaction + shouldHaveContactOffers:shouldHaveContactOffers + maxRangeSize:maxRangeSize + blockingSafetyNumberChanges:blockingSafetyNumberChanges + nonBlockingSafetyNumberChanges:nonBlockingSafetyNumberChanges + hideUnreadMessagesIndicator:hideUnreadMessagesIndicator + firstUnseenInteractionTimestamp:firstUnseenInteractionTimestamp]; // Determine the position of the focus message _after_ performing any mutations // around dynamic interactions. @@ -539,14 +534,14 @@ NS_ASSUME_NONNULL_BEGIN } + (void)ensureUnreadIndicator:(ThreadDynamicInteractions *)dynamicInteractions - thread:(TSThread *)thread - transaction:(YapDatabaseReadWriteTransaction *)transaction - shouldHaveContactOffers:(BOOL)shouldHaveContactOffers - maxRangeSize:(int)maxRangeSize - blockingSafetyNumberChanges:(NSArray *)blockingSafetyNumberChanges - nonBlockingSafetyNumberChanges:(NSArray *)nonBlockingSafetyNumberChanges - hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator - firstUnseenSortId:(nullable NSNumber *)firstUnseenSortId + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction + shouldHaveContactOffers:(BOOL)shouldHaveContactOffers + maxRangeSize:(int)maxRangeSize + blockingSafetyNumberChanges:(NSArray *)blockingSafetyNumberChanges + nonBlockingSafetyNumberChanges:(NSArray *)nonBlockingSafetyNumberChanges + hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator + firstUnseenInteractionTimestamp:(nullable NSNumber *)firstUnseenInteractionTimestamp { OWSAssertDebug(dynamicInteractions); OWSAssertDebug(thread); @@ -557,7 +552,7 @@ NS_ASSUME_NONNULL_BEGIN if (hideUnreadMessagesIndicator) { return; } - if (!firstUnseenSortId) { + if (!firstUnseenInteractionTimestamp) { // If there are no unseen interactions, don't show an unread indicator. return; } @@ -593,7 +588,7 @@ NS_ASSUME_NONNULL_BEGIN return; } - if (interaction.sortId < firstUnseenSortId.unsignedLongLongValue) { + if (interaction.timestampForSorting < firstUnseenInteractionTimestamp.unsignedLongLongValue) { // By default we want the unread indicator to appear just before // the first unread message. *stop = YES; @@ -625,12 +620,13 @@ NS_ASSUME_NONNULL_BEGIN if (hasMoreUnseenMessages) { NSMutableSet *missingUnseenSafetyNumberChanges = [NSMutableSet set]; for (TSInvalidIdentityKeyErrorMessage *safetyNumberChange in blockingSafetyNumberChanges) { - BOOL isUnseen = safetyNumberChange.sortId >= firstUnseenSortId.unsignedLongLongValue; + BOOL isUnseen + = safetyNumberChange.timestampForSorting >= firstUnseenInteractionTimestamp.unsignedLongLongValue; if (!isUnseen) { continue; } - - BOOL isMissing = safetyNumberChange.sortId < interactionAfterUnreadIndicator.sortId; + BOOL isMissing + = safetyNumberChange.timestampForSorting < interactionAfterUnreadIndicator.timestampForSorting; if (!isMissing) { continue; } @@ -655,12 +651,13 @@ NS_ASSUME_NONNULL_BEGIN unreadIndicatorPosition++; } - dynamicInteractions.unreadIndicator = - [[OWSUnreadIndicator alloc] initWithFirstUnseenSortId:firstUnseenSortId.unsignedLongLongValue - hasMoreUnseenMessages:hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:unreadIndicatorPosition]; - OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.firstUnseenSortId); + dynamicInteractions.unreadIndicator = [[OWSUnreadIndicator alloc] + initUnreadIndicatorWithTimestamp:interactionAfterUnreadIndicator.timestampForSorting + hasMoreUnseenMessages:hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:unreadIndicatorPosition + firstUnseenInteractionTimestamp:firstUnseenInteractionTimestamp.unsignedLongLongValue]; + OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.timestamp); } + (nullable NSNumber *)focusMessagePositionForThread:(TSThread *)thread diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index c7040d28d3..207d5c9c85 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -18,8 +18,6 @@ NS_ASSUME_NONNULL_BEGIN // YES IFF this thread has ever had a message. @property (nonatomic) BOOL hasEverHadMessage; -@property (nonatomic, readonly) NSDate *creationDate; -@property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting; /** * Whether the object is a group thread or not. @@ -61,10 +59,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Get all messages in the thread we weren't able to decrypt */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (NSArray *)receivedMessagesForInvalidKey:(NSData *)key; -#pragma clang diagnostic pop - (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(unreadMessageCount(transaction:)); @@ -73,6 +68,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; +/** + * Returns the latest date of a message in the thread or the thread creation date if there are no messages in that + *thread. + * + * @return The date of the last message or thread creation date. + */ +- (NSDate *)lastMessageDate; + /** * Returns the string that will be displayed typically in a conversations view as a preview of the last message *received in this thread. @@ -96,19 +99,31 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Archival /** - * @return YES if no new messages have been sent or received since the thread was last archived. + * Returns the last date at which a string was archived or nil if the thread was never archived or brought back to the + *inbox. + * + * @return Last archival date. */ -- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (nullable NSDate *)archivalDate; /** - * Archives a thread + * Archives a thread with the current date. * * @param transaction Database transaction. */ - (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; /** - * Unarchives a thread + * Archives a thread with the reference date. This is currently only used for migrating older data that has already + * been archived. + * + * @param transaction Database transaction. + * @param date Date at which the thread was archived. + */ +- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction referenceDate:(NSDate *)date; + +/** + * Unarchives a thread that was archived previously. * * @param transaction Database transaction. */ diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index 59cd13007f..59f298972a 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -15,7 +15,6 @@ #import "TSInteraction.h" #import "TSInvalidIdentityKeyReceivingErrorMessage.h" #import "TSOutgoingMessage.h" -#import #import NS_ASSUME_NONNULL_BEGIN @@ -23,8 +22,10 @@ NS_ASSUME_NONNULL_BEGIN @interface TSThread () @property (nonatomic) NSDate *creationDate; +@property (nonatomic, copy, nullable) NSDate *archivalDate; @property (nonatomic) NSString *conversationColorName; -@property (nonatomic) NSNumber *archivedAsOfMessageSortId; +@property (nonatomic, nullable) NSDate *lastMessageDate; + @property (nonatomic, copy, nullable) NSString *messageDraft; @property (atomic, nullable) NSDate *mutedUntilDate; @@ -43,6 +44,8 @@ NS_ASSUME_NONNULL_BEGIN self = [super initWithUniqueId:uniqueId]; if (self) { + _archivalDate = nil; + _lastMessageDate = nil; _creationDate = [NSDate date]; _messageDraft = nil; @@ -74,12 +77,7 @@ NS_ASSUME_NONNULL_BEGIN _conversationColorName = [self.class stableConversationColorNameForString:self.uniqueId]; } } - - NSDate *_Nullable lastMessageDate = [coder decodeObjectOfClass:NSDate.class forKey:@"lastMessageDate"]; - NSDate *_Nullable archivalDate = [coder decodeObjectOfClass:NSDate.class forKey:@"archivalDate"]; - _isArchivedByLegacyTimestampForSorting = - [self.class legacyIsArchivedWithLastMessageDate:lastMessageDate archivalDate:archivalDate]; - + return self; } @@ -209,8 +207,6 @@ NS_ASSUME_NONNULL_BEGIN return [interactions copy]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (NSArray *)receivedMessagesForInvalidKey:(NSData *)key { NSMutableArray *errorMessages = [NSMutableArray new]; @@ -225,7 +221,6 @@ NS_ASSUME_NONNULL_BEGIN return [errorMessages copy]; } -#pragma clang diagnostic pop - (NSUInteger)numberOfInteractions { @@ -303,6 +298,14 @@ NS_ASSUME_NONNULL_BEGIN return last; } +- (NSDate *)lastMessageDate { + if (_lastMessageDate) { + return _lastMessageDate; + } else { + return _creationDate; + } +} + - (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction { TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction]; @@ -348,8 +351,12 @@ NS_ASSUME_NONNULL_BEGIN return; } - if (!self.hasEverHadMessage) { - self.hasEverHadMessage = YES; + self.hasEverHadMessage = YES; + + NSDate *lastMessageDate = [lastMessage dateForSorting]; + if (!_lastMessageDate || [lastMessageDate timeIntervalSinceDate:self.lastMessageDate] > 0) { + _lastMessageDate = lastMessageDate; + [self saveWithTransaction:transaction]; } } @@ -374,54 +381,30 @@ NS_ASSUME_NONNULL_BEGIN } } -#pragma mark - Archival +#pragma mark Archival -- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (nullable NSDate *)archivalDate { - if (!self.archivedAsOfMessageSortId) { - return NO; - } - - TSInteraction *_Nullable latestInteraction = [self lastInteractionForInboxWithTransaction:transaction]; - uint64_t latestSortIdForInbox = latestInteraction ? latestInteraction.sortId : 0; - return self.archivedAsOfMessageSortId.unsignedLongLongValue >= latestSortIdForInbox; + return _archivalDate; } -+ (BOOL)legacyIsArchivedWithLastMessageDate:(nullable NSDate *)lastMessageDate - archivalDate:(nullable NSDate *)archivalDate -{ - if (!archivalDate) { - return NO; - } - - if (!lastMessageDate) { - return YES; - } - - return [archivalDate compare:lastMessageDate] != NSOrderedAscending; +- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + [self archiveThreadWithTransaction:transaction referenceDate:[NSDate date]]; } -- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - [self applyChangeToSelfAndLatestCopy:transaction - changeBlock:^(TSThread *thread) { - uint64_t latestId = [SSKIncrementingIdFinder previousIdWithKey:TSInteraction.collection - transaction:transaction]; - thread.archivedAsOfMessageSortId = @(latestId); - }]; - +- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction referenceDate:(NSDate *)date { [self markAllAsReadWithTransaction:transaction]; + _archivalDate = date; + + [self saveWithTransaction:transaction]; } -- (void)unarchiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - [self applyChangeToSelfAndLatestCopy:transaction - changeBlock:^(TSThread *thread) { - thread.archivedAsOfMessageSortId = nil; - }]; +- (void)unarchiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + _archivalDate = nil; + [self saveWithTransaction:transaction]; } -#pragma mark - Drafts +#pragma mark Drafts - (NSString *)currentDraftWithTransaction:(YapDatabaseReadTransaction *)transaction { TSThread *thread = [TSThread fetchObjectWithUniqueID:self.uniqueId transaction:transaction]; diff --git a/SignalServiceKit/src/Devices/OWSReadReceiptsForSenderMessage.m b/SignalServiceKit/src/Devices/OWSReadReceiptsForSenderMessage.m index 88c2ffcec2..4d06a8d0ef 100644 --- a/SignalServiceKit/src/Devices/OWSReadReceiptsForSenderMessage.m +++ b/SignalServiceKit/src/Devices/OWSReadReceiptsForSenderMessage.m @@ -21,7 +21,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithThread:(nullable TSThread *)thread messageTimestamps:(NSArray *)messageTimestamps { - // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index c83711aeb3..58ac8127d4 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -70,8 +70,6 @@ NS_ASSUME_NONNULL_BEGIN if (transcript.isEndSessionMessage) { OWSLogInfo(@"EndSession was sent to recipient: %@.", transcript.recipientId); [self.primaryStorage deleteAllSessionsForContact:transcript.recipientId protocolContext:transaction]; - - // MJK TODO - we don't use this timestamp, safe to remove [[[TSInfoMessage alloc] initWithTimestamp:transcript.timestamp inThread:transcript.thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; @@ -129,13 +127,12 @@ NS_ASSUME_NONNULL_BEGIN } } - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:outgoingMessage.expiresInSeconds - thread:transcript.thread - createdByRemoteRecipientId:nil - createdInExistingGroup:NO - transaction:transaction]; - if (transcript.isExpirationTimerUpdate) { + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage + contactsManager:self.contactsManager + transaction:transaction]; + + // early return to avoid saving an empty incoming message. OWSAssertDebug(transcript.body.length == 0); OWSAssertDebug(outgoingMessage.attachmentIds.count == 0); @@ -150,6 +147,9 @@ NS_ASSUME_NONNULL_BEGIN [outgoingMessage saveWithTransaction:transaction]; [outgoingMessage updateWithWasSentFromLinkedDeviceWithTransaction:transaction]; + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage + contactsManager:self.contactsManager + transaction:transaction]; [[OWSDisappearingMessagesJob sharedJob] startAnyExpirationForMessage:outgoingMessage expirationStartedAt:transcript.expirationStartedAt transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m index 441e4b3bcc..f457b3c85d 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m @@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init { - // MJK TODO - remove SenderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:nil messageBody:nil diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m index 06c9ab0b18..7f491f9d25 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m @@ -20,7 +20,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithThread:(nullable TSThread *)thread groupId:(NSData *)groupId { - // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h index b530124faf..52b2bb3738 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h @@ -16,7 +16,6 @@ NS_ASSUME_NONNULL_BEGIN /** * @param remoteName is nil when created by the local user */ -// MJK TODO - can we remove sendertimestamp here - (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread configuration:(OWSDisappearingMessagesConfiguration *)configuration diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index c10ab76fe5..da4be63772 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -74,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN return [NSString stringWithFormat:infoFormat, self.createdByRemoteName]; } } else { - // Changed by localNumber on this device or via synced transcript + // Changed by local request if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) { NSString *infoFormat = NSLocalizedString(@"YOU_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION", @"Info message embedding a {{time amount}}, see the *_TIME_AMOUNT strings for context."); diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h index e28eadc858..37c9a3be8e 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h @@ -10,7 +10,6 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSDisappearingMessagesConfigurationMessage : TSOutgoingMessage -// MJK TODO - remove senderTimestamp - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread messageBody:(nullable NSString *)body diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m index caf0a34f22..cdeff48117 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m @@ -26,7 +26,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread { - // MJK TODO - remove sender timestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h index 58d5f80d6e..035da057d0 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h @@ -11,7 +11,6 @@ NS_ASSUME_NONNULL_BEGIN typedef NSData *_Nonnull (^DynamicOutgoingMessageBlock)(SignalRecipient *); -/// This class is only used in debug tools @interface OWSDynamicOutgoingMessage : TSOutgoingMessage - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m index de64752d2b..bfc008f15a 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m @@ -23,7 +23,6 @@ NS_ASSUME_NONNULL_BEGIN return [self initWithPlainTextDataBlock:block timestamp:[NSDate ows_millisecondTimeStamp] thread:thread]; } -// MJK TODO can we remove sender timestamp? - (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block timestamp:(uint64_t)timestamp thread:(nullable TSThread *)thread diff --git a/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h index a308120126..c3ca4a2556 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h @@ -20,7 +20,6 @@ NS_SWIFT_NAME(EndSessionMessage) quotedMessage:(nullable TSQuotedMessage *)quotedMessage contactShare:(nullable OWSContact *)contactShare NS_UNAVAILABLE; -// MJK TODO can we remove the sender timestamp? - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h index 775bd14c44..2f1a3f75ae 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSRecipientIdentity.h" diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m index ca57a62121..31c33e8036 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSVerificationStateChangeMessage.h" diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index 8d6e18b996..e29a6d6ecc 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -51,6 +51,10 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) { failedMessageType:(TSErrorMessageType)errorMessageType recipientId:(nullable NSString *)recipientId NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithTimestamp:(uint64_t)timestamp + inThread:(nullable TSThread *)thread + failedMessageType:(TSErrorMessageType)errorMessageType; + + (instancetype)corruptedMessageWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index fe34225f93..c28750254c 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -91,11 +91,14 @@ NSUInteger TSErrorMessageSchemaVersion = 1; TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - // Legit usage of senderTimestamp. We don't actually currently surface it in the UI, but it serves as - // a reference to the envelope which we failed to process. return [self initWithTimestamp:envelope.timestamp inThread:contactThread failedMessageType:errorMessageType]; } +- (instancetype)initWithFailedMessageType:(TSErrorMessageType)errorMessageType +{ + return [self initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:nil failedMessageType:errorMessageType]; +} + - (OWSInteractionType)interactionType { return OWSInteractionType_Error; @@ -153,10 +156,7 @@ NSUInteger TSErrorMessageSchemaVersion = 1; + (instancetype)corruptedMessageInUnknownThread { - // MJK TODO - Seems like we could safely remove this timestamp - return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:nil - failedMessageType:TSErrorMessageInvalidMessage]; + return [[self alloc] initWithFailedMessageType:TSErrorMessageInvalidMessage]; } + (instancetype)invalidVersionWithEnvelope:(SSKProtoEnvelope *)envelope @@ -184,7 +184,6 @@ NSUInteger TSErrorMessageSchemaVersion = 1; + (instancetype)nonblockingIdentityChangeInThread:(TSThread *)thread recipientId:(NSString *)recipientId { - // MJK TODO - should be safe to remove this senderTimestamp return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageNonBlockingIdentityChange diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h index 77e5a39f46..e2336d800c 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h @@ -4,16 +4,17 @@ #import "TSErrorMessage.h" -NS_ASSUME_NONNULL_BEGIN - @interface TSErrorMessage () - (instancetype)initWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread + inThread:(TSThread *)thread failedMessageType:(TSErrorMessageType)errorMessageType NS_DESIGNATED_INITIALIZER; @property (atomic, nullable) NSData *envelopeData; -@end +@property NSDictionary *pendingOutgoingMessage; -NS_ASSUME_NONNULL_END +#define TSPendingOutgoingMessageKey @"TSPendingOutgoingMessageKey" +#define TSPendingOutgoingMessageRecipientKey @"TSPendingOutgoingMessageRecipientKey" + +@end diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m index f1b3741596..557f97e9a6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m @@ -48,7 +48,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1; inThread:(TSThread *)thread messageType:(TSInfoMessageType)infoMessage { - // MJK TODO - remove senderTimestamp self = [super initMessageWithTimestamp:timestamp inThread:thread messageBody:nil @@ -101,7 +100,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1; OWSAssertDebug(thread); OWSAssertDebug(recipientId); - // MJK TODO - remove senderTimestamp return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageUserNotRegistered diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h index 7487c76a75..a5b29b147e 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h @@ -33,9 +33,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); @property (nonatomic, readonly) NSString *uniqueThreadId; @property (nonatomic, readonly) TSThread *thread; @property (nonatomic, readonly) uint64_t timestamp; -@property (nonatomic, readonly) uint64_t sortId; -@property (nonatomic, readonly) uint64_t receivedAtTimestamp; -- (NSDate *)receivedAtDate; - (OWSInteractionType)interactionType; @@ -57,8 +54,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); filter:(BOOL (^_Nonnull)(TSInteraction *))filter withTransaction:(YapDatabaseReadTransaction *)transaction; -- (NSDate *)dateForLegacySorting; -- (uint64_t)timestampForLegacySorting; +- (NSDate *)dateForSorting; +- (uint64_t)timestampForSorting; - (NSComparisonResult)compareForSorting:(TSInteraction *)other; // "Dynamic" interactions are not messages or static events (like @@ -69,9 +66,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); // unseen message indicators, etc. - (BOOL)isDynamicInteraction; -- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - NS_SWIFT_NAME(saveNextSortId(transaction:)); - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m index a3fb718469..d76cfe9e5a 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m @@ -7,7 +7,6 @@ #import "OWSPrimaryStorage+messageIDs.h" #import "TSDatabaseSecondaryIndexes.h" #import "TSThread.h" -#import NS_ASSUME_NONNULL_BEGIN @@ -31,12 +30,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) } } -@interface TSInteraction () - -@property (nonatomic) uint64_t sortId; - -@end - @implementation TSInteraction + (NSArray *)interactionsWithTimestamp:(uint64_t)timestamp @@ -64,6 +57,7 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) [TSDatabaseSecondaryIndexes enumerateMessagesWithTimestamp:timestamp withBlock:^(NSString *collection, NSString *key, BOOL *stop) { + TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:key transaction:transaction]; if (!filter(interaction)) { @@ -92,37 +86,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) _timestamp = timestamp; _uniqueThreadId = thread.uniqueId; - _receivedAtTimestamp = [NSDate ows_millisecondTimeStamp]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - self = [super initWithCoder:coder]; - if (!self) { - return nil; - } - - // Previously the receivedAtTimestamp field lived on TSMessage, but we've moved it up - // to the TSInteraction superclass. - if (_receivedAtTimestamp == 0) { - // Upgrade from the older "TSMessage.receivedAtDate" and "TSMessage.receivedAt" properties if - // necessary. - NSDate *receivedAtDate = [coder decodeObjectForKey:@"receivedAtDate"]; - if (!receivedAtDate) { - receivedAtDate = [coder decodeObjectForKey:@"receivedAt"]; - } - - if (receivedAtDate) { - _receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate]; - } - - // For TSInteractions which are not TSMessage's, the timestamp *is* the receivedAtTimestamp - if (_receivedAtTimestamp == 0) { - _receivedAtTimestamp = _timestamp; - } - } return self; } @@ -147,31 +110,26 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) #pragma mark Date operations -- (NSDate *)dateForLegacySorting +- (NSDate *)dateForSorting { - return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForLegacySorting]; + return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForSorting]; } -- (uint64_t)timestampForLegacySorting +- (uint64_t)timestampForSorting { return self.timestamp; } -- (NSDate *)receivedAtDate -{ - return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp]; -} - - (NSComparisonResult)compareForSorting:(TSInteraction *)other { OWSAssertDebug(other); - uint64_t sortId1 = self.sortId; - uint64_t sortId2 = self.sortId; + uint64_t timestamp1 = self.timestampForSorting; + uint64_t timestamp2 = other.timestampForSorting; - if (sortId1 > sortId2) { + if (timestamp1 > timestamp2) { return NSOrderedDescending; - } else if (sortId1 < sortId2) { + } else if (timestamp1 < timestamp2) { return NSOrderedAscending; } else { return NSOrderedSame; @@ -193,20 +151,14 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) (unsigned long)self.timestamp]; } -- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - // MJK can we remove this? We can't trust the legacy order of this id field. Any reason not to use UUID like for - // other objects? +- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { if (!self.uniqueId) { self.uniqueId = [OWSPrimaryStorage getAndIncrementMessageIdWithTransaction:transaction]; } - if (self.sortId == 0) { - self.sortId = [SSKIncrementingIdFinder nextIdWithKey:[TSInteraction collection] transaction:transaction]; - } [super saveWithTransaction:transaction]; - TSThread *fetchedThread = [self threadWithTransaction:transaction]; + TSThread *fetchedThread = [TSThread fetchObjectWithUniqueID:self.uniqueThreadId transaction:transaction]; [fetchedThread updateWithLastMessage:self transaction:transaction]; } @@ -223,20 +175,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) return NO; } -#pragma mark - sorting migration - -- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - if (self.sortId != 0) { - // This could happen if something else in our startup process saved the interaction - // e.g. another migration ran. - // During the migration, since we're enumerating the interactions in the proper order, - // we want to ignore any previously assigned sortId - self.sortId = 0; - } - [self saveWithTransaction:transaction]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 875c247e18..06ac2abe2d 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -47,6 +47,13 @@ static const NSUInteger OWSMessageSchemaVersion = 4; */ @property (nonatomic, readonly) NSUInteger schemaVersion; +// The timestamp property is populated by the envelope, +// which is created by the sender. +// +// We typically want to order messages locally by when +// they were received & decrypted, not by when they were sent. +@property (nonatomic) uint64_t receivedAtTimestamp; + @end #pragma mark - @@ -75,6 +82,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _expiresInSeconds = expiresInSeconds; _expireStartedAt = expireStartedAt; [self updateExpiresAt]; + _receivedAtTimestamp = [NSDate ows_millisecondTimeStamp]; _quotedMessage = quotedMessage; _contactShare = contactShare; @@ -127,6 +135,18 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _attachmentIds = [NSMutableArray new]; } + if (_receivedAtTimestamp == 0) { + // Upgrade from the older "receivedAtDate" and "receivedAt" properties if + // necessary. + NSDate *receivedAtDate = [coder decodeObjectForKey:@"receivedAtDate"]; + if (!receivedAtDate) { + receivedAtDate = [coder decodeObjectForKey:@"receivedAt"]; + } + if (receivedAtDate) { + _receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate]; + } + } + _schemaVersion = OWSMessageSchemaVersion; return self; @@ -320,7 +340,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return self.expiresInSeconds > 0; } -- (uint64_t)timestampForLegacySorting +- (uint64_t)timestampForSorting { if ([self shouldUseReceiptDateForSorting] && self.receivedAtTimestamp > 0) { return self.receivedAtTimestamp; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index 3ae50df069..e23a4de109 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -77,7 +77,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { quotedMessage:(nullable TSQuotedMessage *)quotedMessage contactShare:(nullable OWSContact *)contactShare NS_UNAVAILABLE; -// MJK TODO - Can we remove the sender timestamp param? - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread messageBody:(nullable NSString *)body diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index e5e21ae917..717b89b068 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -261,7 +261,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt [attachmentIds addObject:attachmentId]; } - // MJK TODO remove SenderTimestamp? return [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:body @@ -278,7 +277,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage expiresInSeconds:(uint32_t)expiresInSeconds; { - // MJK TODO remove SenderTimestamp? return [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m index d07a112f13..27a6a408ad 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m @@ -208,7 +208,6 @@ NS_ASSUME_NONNULL_BEGIN return nil; } - // Legit usage of senderTimestamp - this class references the message it is quoting by it's sender timestamp return [[TSQuotedMessage alloc] initWithTimestamp:timestamp authorId:authorId body:body @@ -230,6 +229,7 @@ NS_ASSUME_NONNULL_BEGIN NSArray *quotedMessages = (NSArray *)[TSInteraction interactionsWithTimestamp:timestamp filter:^BOOL(TSInteraction *interaction) { + if (![threadId isEqual:interaction.uniqueThreadId]) { return NO; } @@ -243,6 +243,7 @@ NS_ASSUME_NONNULL_BEGIN // ignore other interaction types return NO; } + } withTransaction:transaction]; diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h index de4f023aaf..8aca7e224b 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h @@ -10,13 +10,10 @@ NS_ASSUME_NONNULL_BEGIN // DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may // exist, so we should keep this class around to honor their old behavior. -__attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage : TSInvalidIdentityKeyErrorMessage -#ifdef DEBUG + (nullable instancetype)untrustedKeyWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; -#endif @end diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 3d49749607..c8c55173d5 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN -__attribute__((deprecated)) +/// TODO we can eventually deprecate this, since incoming messages are now always decrypted. @interface TSInvalidIdentityKeyReceivingErrorMessage () @property (nonatomic, readonly, copy) NSString *authorId; @@ -33,20 +33,15 @@ __attribute__((deprecated)) @synthesize envelopeData = _envelopeData; -#ifdef DEBUG -// We no longer create these messages, but they might exist on legacy clients so it's useful to be able to -// create them with the debug UI + (nullable instancetype)untrustedKeyWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction { TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - - // Legit usage of senderTimestamp, references message which failed to decrypt TSInvalidIdentityKeyReceivingErrorMessage *errorMessage = - [[self alloc] initForUnknownIdentityKeyWithTimestamp:envelope.timestamp - inThread:contactThread - incomingEnvelope:envelope]; + [[self alloc] initForUnknownIdentityKeyWithTimestamp:envelope.timestamp + inThread:contactThread + incomingEnvelope:envelope]; return errorMessage; } @@ -70,7 +65,6 @@ __attribute__((deprecated)) return self; } -#endif - (nullable SSKProtoEnvelope *)envelope { diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h index dd9e9f7c04..08efb22141 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "TSInvalidIdentityKeyErrorMessage.h" @@ -13,9 +13,6 @@ NS_ASSUME_NONNULL_BEGIN extern NSString *TSInvalidPreKeyBundleKey; extern NSString *TSInvalidRecipientKey; -// DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may -// exist, so we should keep this class around to honor their old behavior. -__attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : TSInvalidIdentityKeyErrorMessage @property (nonatomic, readonly) NSString *messageId; diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m index 43f792c684..01e4d179c9 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m @@ -24,12 +24,26 @@ NSString *TSInvalidRecipientKey = @"TSInvalidRecipientKey"; @end -// DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may -// exist, so we should keep this class around to honor their old behavior. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation TSInvalidIdentityKeySendingErrorMessage -#pragma clang diagnostic pop + +- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message + inThread:(TSThread *)thread + forRecipient:(NSString *)recipientId + preKeyBundle:(PreKeyBundle *)preKeyBundle +{ + // We want the error message to appear after the message. + self = [super initWithTimestamp:message.timestamp + 1 + inThread:thread + failedMessageType:TSErrorMessageWrongTrustedIdentityKey + recipientId:recipientId]; + + if (self) { + _messageId = message.uniqueId; + _preKeyBundle = preKeyBundle; + } + + return self; +} - (void)acceptNewIdentityKey { diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h index b6d5408cd3..aec3bc0366 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h @@ -1,19 +1,14 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "TSInfoMessage.h" NS_ASSUME_NONNULL_BEGIN -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSAddToContactsOfferMessage : TSInfoMessage -+ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId; ++ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId; @property (nonatomic, readonly) NSString *contactId; diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m index af1b648c86..b09686a0ba 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSAddToContactsOfferMessage.h" @@ -14,16 +14,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSAddToContactsOfferMessage -#pragma clang diagnostic pop -+ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId ++ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId { return [[OWSAddToContactsOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId]; } diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h index 368c930852..0e003ca7a2 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h @@ -1,16 +1,14 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "TSInfoMessage.h" NS_ASSUME_NONNULL_BEGIN -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSAddToProfileWhitelistOfferMessage : TSInfoMessage +@interface OWSAddToProfileWhitelistOfferMessage : TSInfoMessage -+ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread; ++ (instancetype)addToProfileWhitelistOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread; @property (nonatomic, readonly) NSString *contactId; diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m index 0ca6114094..7a59d80ae4 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSAddToProfileWhitelistOfferMessage.h" @@ -7,14 +7,9 @@ NS_ASSUME_NONNULL_BEGIN -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSAddToProfileWhitelistOfferMessage -#pragma clang diagnostic pop -+ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread ++ (instancetype)addToProfileWhitelistOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread { return [[OWSAddToProfileWhitelistOfferMessage alloc] initWithTimestamp:timestamp diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h index 0eb504c331..af70638e39 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h @@ -21,23 +21,41 @@ NS_ASSUME_NONNULL_BEGIN expirationStartedAt:(uint64_t)expirationStartedAt transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction; +/** + * Synchronize our disappearing messages settings with that of the given message. Useful so we can + * become eventually consistent with remote senders. + * + * @param message + * Can be an expiring or non expiring message. We match the expiration timer of the message, including disabling + * expiring messages if the message is not an expiring message. + * + * @param contactsManager + * Provides the contact name responsible for any configuration changes in an info message. + */ +- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message + contactsManager:(id)contactsManager + transaction:(YapDatabaseReadWriteTransaction *)transaction; + /** * Synchronize our disappearing messages settings with that of the given message. Useful so we can * become eventually consistent with remote senders. * * @param duration * Can be 0, indicating a non-expiring message, or greater, indicating an expiring message. We match the expiration - * timer of the message, including disabling expiring messages if the message is not an expiring message. + * timer of the message, including disabling expiring messages if the message is not an expiring message. * - * @param remoteRecipientId - * nil for outgoing messages, otherwise the recipientId of the sender + * @param timestampForSorting + * timestampForSorting of the message before which we want to appear. * + * @param remoteContactName + * nil for outgoing messages, otherwise the name of the sender * @param createdInExistingGroup * YES when being added to a group which already has DM enabled, otherwise NO */ - (void)becomeConsistentWithDisappearingDuration:(uint32_t)duration thread:(TSThread *)thread - createdByRemoteRecipientId:(nullable NSString *)remoteRecipientId + appearBeforeTimestamp:(uint64_t)timestampForSorting + createdByRemoteContactName:(nullable NSString *)remoteContactName createdInExistingGroup:(BOOL)createdInExistingGroup transaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m index 28f875fb25..5bacb50d6c 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m @@ -13,7 +13,6 @@ #import "OWSDisappearingMessagesConfiguration.h" #import "OWSDisappearingMessagesFinder.h" #import "OWSPrimaryStorage.h" -#import "SSKEnvironment.h" #import "TSIncomingMessage.h" #import "TSMessage.h" #import "TSThread.h" @@ -111,15 +110,6 @@ void AssertIsOnDisappearingMessagesQueue() return queue; } -#pragma mark - Dependencies - -- (id)contactsManager -{ - return SSKEnvironment.shared.contactsManager; -} - -#pragma mark - - - (NSUInteger)deleteExpiredMessages { AssertIsOnDisappearingMessagesQueue(); @@ -203,25 +193,38 @@ void AssertIsOnDisappearingMessagesQueue() }]; } +- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message + contactsManager:(id)contactsManager + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + TSThread *thread = [message threadWithTransaction:transaction]; + NSString *remoteContactName = nil; + if ([message isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message; + remoteContactName = [contactsManager displayNameForPhoneIdentifier:incomingMessage.messageAuthorId]; + } -#pragma mark - Apply Remote Configuration + [self becomeConsistentWithDisappearingDuration:message.expiresInSeconds + thread:thread + appearBeforeTimestamp:message.timestampForSorting + createdByRemoteContactName:remoteContactName + createdInExistingGroup:NO + transaction:transaction]; +} - (void)becomeConsistentWithDisappearingDuration:(uint32_t)duration thread:(TSThread *)thread - createdByRemoteRecipientId:(nullable NSString *)remoteRecipientId + appearBeforeTimestamp:(uint64_t)timestampForSorting + createdByRemoteContactName:(nullable NSString *)remoteContactName createdInExistingGroup:(BOOL)createdInExistingGroup transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssertDebug(thread); + OWSAssertDebug(timestampForSorting > 0); OWSAssertDebug(transaction); OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - NSString *_Nullable remoteContactName = nil; - if (remoteRecipientId) { - remoteContactName = [self.contactsManager displayNameForPhoneIdentifier:remoteRecipientId]; - } - // Become eventually consistent in the case that the remote changed their settings at the same time. // Also in case remote doesn't support expiring messages OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration = @@ -243,9 +246,9 @@ void AssertIsOnDisappearingMessagesQueue() [disappearingMessagesConfiguration saveWithTransaction:transaction]; - // MJK TODO - should be safe to remove this senderTimestamp + // We want the info message to appear _before_ the message. OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = - [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:timestampForSorting - 1 thread:thread configuration:disappearingMessagesConfiguration createdByRemoteName:remoteContactName @@ -256,8 +259,6 @@ void AssertIsOnDisappearingMessagesQueue() backgroundTask = nil; } -#pragma mark - - - (void)startIfNecessary { dispatch_async(dispatch_get_main_queue(), ^{ @@ -393,7 +394,7 @@ void AssertIsOnDisappearingMessagesQueue() OWSFailDebug(@"starting old timer for message timestamp: %lu", (unsigned long)message.timestamp); // We don't know when it was actually read, so assume it was read as soon as it was received. - uint64_t readTimeBestGuess = message.receivedAtTimestamp; + uint64_t readTimeBestGuess = message.timestampForSorting; [self startAnyExpirationForMessage:message expirationStartedAt:readTimeBestGuess transaction:transaction]; } transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.m b/SignalServiceKit/src/Messages/OWSIdentityManager.m index 444f1cab8b..922115dc87 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.m +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.m @@ -528,7 +528,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa [messages addObject:[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId]]; } - // MJK TODO - why not save immediately, why build up this array? for (TSMessage *message in messages) { [message saveWithTransaction:transaction]; } @@ -846,7 +845,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; OWSAssertDebug(contactThread); - // MJK TODO - should be safe to remove senderTimestamp [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:contactThread recipientId:recipientId @@ -855,7 +853,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId transaction:transaction]) { - // MJK TODO - should be safe to remove senderTimestamp [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:groupThread @@ -864,7 +861,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa isLocalChange:isLocalChange]]; } - // MJK TODO - why not save in-line, vs storing in an array and saving the array? for (TSMessage *message in messages) { [message saveWithTransaction:transaction]; } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 9d5e6f3b99..2ff730a528 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -879,7 +879,6 @@ NS_ASSUME_NONNULL_BEGIN TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - // MJK TODO - safe to remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; @@ -928,7 +927,6 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(disappearingMessagesConfiguration); [disappearingMessagesConfiguration saveWithTransaction:transaction]; NSString *name = [self.contactsManager displayNameForPhoneIdentifier:envelope.source]; - // MJK TODO - safe to remove senderTimestamp OWSDisappearingConfigurationUpdateInfoMessage *message = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -1146,6 +1144,8 @@ NS_ASSUME_NONNULL_BEGIN TSGroupThread *newGroupThread = [TSGroupThread getOrCreateThreadWithGroupId:groupId transaction:transaction]; + + uint64_t now = [NSDate ows_millisecondTimeStamp]; TSGroupModel *newGroupModel = [[TSGroupModel alloc] initWithTitle:dataMessage.group.name memberIds:newMemberIds.allObjects image:oldGroupThread.groupModel.groupImage @@ -1155,19 +1155,22 @@ NS_ASSUME_NONNULL_BEGIN newGroupThread.groupModel = newGroupModel; [newGroupThread saveWithTransaction:transaction]; - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer - thread:newGroupThread - createdByRemoteRecipientId:nil - createdInExistingGroup:NO - transaction:transaction]; - - // MJK TODO - should be safe to remove senderTimestamp - TSInfoMessage *infoMessage = [[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + TSInfoMessage *infoMessage = [[TSInfoMessage alloc] initWithTimestamp:now inThread:newGroupThread messageType:TSInfoMessageTypeGroupUpdate customMessage:updateGroupInfo]; [infoMessage saveWithTransaction:transaction]; + if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) { + [[OWSDisappearingMessagesJob sharedJob] + becomeConsistentWithDisappearingDuration:dataMessage.expireTimer + thread:newGroupThread + appearBeforeTimestamp:now + createdByRemoteContactName:nil + createdInExistingGroup:YES + transaction:transaction]; + } + return nil; } case SSKProtoGroupContextTypeQuit: { @@ -1182,7 +1185,6 @@ NS_ASSUME_NONNULL_BEGIN NSString *nameString = [self.contactsManager displayNameForPhoneIdentifier:envelope.source]; NSString *updateGroupInfo = [NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), nameString]; - // MJK TODO - should be safe to remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:oldGroupThread messageType:TSInfoMessageTypeGroupUpdate @@ -1203,12 +1205,6 @@ NS_ASSUME_NONNULL_BEGIN return nil; } - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer - thread:oldGroupThread - createdByRemoteRecipientId:envelope.source - createdInExistingGroup:YES - transaction:transaction]; - TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage thread:oldGroupThread transaction:transaction]; @@ -1218,7 +1214,6 @@ NS_ASSUME_NONNULL_BEGIN groupId, (unsigned long)timestamp); - // Legit usage of senderTimestamp when creating an incoming group message record TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp inThread:oldGroupThread @@ -1254,17 +1249,10 @@ NS_ASSUME_NONNULL_BEGIN TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer - thread:thread - createdByRemoteRecipientId:envelope.source - createdInExistingGroup:NO - transaction:transaction]; - TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage thread:thread transaction:transaction]; - // Legit usage of senderTimestamp when creating incoming message from received envelope TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp inThread:thread @@ -1374,6 +1362,10 @@ NS_ASSUME_NONNULL_BEGIN [OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage transaction:transaction]; + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:incomingMessage + contactsManager:self.contactsManager + transaction:transaction]; + // Update thread preview in inbox [thread touchWithTransaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 06c6403f1f..fc92b82e5c 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -467,7 +467,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { for (NSString *recipientId in message.sendingRecipientIds) { [message updateWithReadRecipientId:recipientId - readTimestamp:message.timestamp + readTimestamp:message.timestampForSorting transaction:transaction]; } }]; @@ -1416,13 +1416,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // TODO: Why is this necessary? [message save]; } else if (message.groupMetaMessage == TSGroupMetaMessageQuit) { - // MJK TODO - remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:message.timestamp inThread:thread messageType:TSInfoMessageTypeGroupQuit customMessage:message.customMessage] save]; } else { - // MJK TODO - remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:message.timestamp inThread:thread messageType:TSInfoMessageTypeGroupUpdate diff --git a/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m b/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m index d32f1ca58e..82fea4cf53 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m @@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithThread:(TSThread *)thread { - // MJK TODO - safe to remove senderTimestamp + // These records aren't saved, but their timestamp is used in the event + // of a failing message send to insert the error at the appropriate place. self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m index 9e69e05747..2f3525a57a 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m @@ -24,7 +24,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithContactThread:(TSContactThread *)contactThread verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage { - // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:contactThread messageBody:nil diff --git a/SignalServiceKit/src/Messages/OWSReadReceiptManager.h b/SignalServiceKit/src/Messages/OWSReadReceiptManager.h index e9500db9e8..f97e32ab7c 100644 --- a/SignalServiceKit/src/Messages/OWSReadReceiptManager.h +++ b/SignalServiceKit/src/Messages/OWSReadReceiptManager.h @@ -69,7 +69,7 @@ extern NSString *const kIncomingMessageMarkedAsReadNotification; // This method can be called from any thread. - (void)messageWasReadLocally:(TSIncomingMessage *)message; -- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread; +- (void)markAsReadLocallyBeforeTimestamp:(uint64_t)timestamp thread:(TSThread *)thread; #pragma mark - Settings diff --git a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m index 832721f31f..c55432b672 100644 --- a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m +++ b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m @@ -281,17 +281,17 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE #pragma mark - Mark as Read Locally -- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread +- (void)markAsReadLocallyBeforeTimestamp:(uint64_t)timestamp thread:(TSThread *)thread { OWSAssertDebug(thread); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self markAsReadBeforeSortId:sortId - thread:thread - readTimestamp:[NSDate ows_millisecondTimeStamp] - wasLocal:YES - transaction:transaction]; + [self markAsReadBeforeTimestamp:timestamp + thread:thread + readTimestamp:[NSDate ows_millisecondTimeStamp] + wasLocal:YES + transaction:transaction]; }]; }); } @@ -493,23 +493,26 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE // Always re-mark the message as read to ensure any earlier read time is applied to disappearing messages. [message markAsReadAtTimestamp:readTimestamp sendReadReceipt:NO transaction:transaction]; - // Also mark any unread messages appearing earlier in the thread as read as well. - [self markAsReadBeforeSortId:message.sortId - thread:[message threadWithTransaction:transaction] - readTimestamp:readTimestamp - wasLocal:NO - transaction:transaction]; + // Also mark any messages appearing earlier in the thread as read. + // + // Use `timestampForSorting` which reflects local received order, rather than `timestamp` + // which reflect sender time. + [self markAsReadBeforeTimestamp:message.timestampForSorting + thread:[message threadWithTransaction:transaction] + readTimestamp:readTimestamp + wasLocal:NO + transaction:transaction]; } #pragma mark - Mark As Read -- (void)markAsReadBeforeSortId:(uint64_t)sortId - thread:(TSThread *)thread - readTimestamp:(uint64_t)readTimestamp - wasLocal:(BOOL)wasLocal - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)markAsReadBeforeTimestamp:(uint64_t)timestamp + thread:(TSThread *)thread + readTimestamp:(uint64_t)readTimestamp + wasLocal:(BOOL)wasLocal + transaction:(YapDatabaseReadWriteTransaction *)transaction { - OWSAssertDebug(sortId > 0); + OWSAssertDebug(timestamp > 0); OWSAssertDebug(thread); OWSAssertDebug(transaction); @@ -533,11 +536,12 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE return; } id possiblyRead = (id)object; - if (possiblyRead.sortId > sortId) { + + if (possiblyRead.timestampForSorting > timestamp) { *stop = YES; return; } - + OWSAssertDebug(!possiblyRead.read); OWSAssertDebug(possiblyRead.expireStartedAt == 0); if (!possiblyRead.read) { diff --git a/SignalServiceKit/src/Messages/OWSReadTracking.h b/SignalServiceKit/src/Messages/OWSReadTracking.h index e3314ea83b..5686e744ff 100644 --- a/SignalServiceKit/src/Messages/OWSReadTracking.h +++ b/SignalServiceKit/src/Messages/OWSReadTracking.h @@ -16,7 +16,7 @@ @property (nonatomic, readonly, getter=wasRead) BOOL read; @property (nonatomic, readonly) uint64_t expireStartedAt; -@property (nonatomic, readonly) uint64_t sortId; +@property (nonatomic, readonly) uint64_t timestampForSorting; @property (nonatomic, readonly) NSString *uniqueThreadId; diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h index 1fd3f2a47d..7d9099dd98 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h @@ -1,16 +1,17 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "TSErrorMessage.h" NS_ASSUME_NONNULL_BEGIN -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSUnknownContactBlockOfferMessage : TSErrorMessage ++ (instancetype)unknownContactBlockOfferMessage:(uint64_t)timestamp + thread:(TSThread *)thread + contactId:(NSString *)contactId; + @property (nonatomic, readonly) NSString *contactId; @end diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m index 3d11291cf6..3c9de57e1e 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSUnknownContactBlockOfferMessage.h" @@ -14,12 +14,25 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSUnknownContactBlockOfferMessage -#pragma clang diagnostic pop + ++ (instancetype)unknownContactBlockOfferMessage:(uint64_t)timestamp + thread:(TSThread *)thread + contactId:(NSString *)contactId +{ + return [[OWSUnknownContactBlockOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId]; +} + +- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId +{ + self = [super initWithTimestamp:timestamp inThread:thread failedMessageType:TSErrorMessageUnknownContactBlockOffer]; + + if (self) { + _contactId = contactId; + } + + return self; +} - (BOOL)shouldUseReceiptDateForSorting { diff --git a/SignalServiceKit/src/Storage/IncrementingIdFinder.swift b/SignalServiceKit/src/Storage/IncrementingIdFinder.swift deleted file mode 100644 index d8f06970c7..0000000000 --- a/SignalServiceKit/src/Storage/IncrementingIdFinder.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -public class SSKIncrementingIdFinder: NSObject { - - private static let collectionName = "IncrementingIdCollection" - - @objc - public class func previousId(key: String, transaction: YapDatabaseReadTransaction) -> UInt64 { - let previousId: UInt64 = transaction.object(forKey: key, inCollection: collectionName) as? UInt64 ?? 0 - return previousId - } - - @objc - public class func nextId(key: String, transaction: YapDatabaseReadWriteTransaction) -> UInt64 { - let previousId: UInt64 = transaction.object(forKey: key, inCollection: collectionName) as? UInt64 ?? 0 - let nextId: UInt64 = previousId + 1 - - transaction.setObject(nextId, forKey: key, inCollection: collectionName) - Logger.debug("key: \(key) nextId: \(nextId)") - return nextId - } -} diff --git a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m index ad82f345e1..4c9c0a11ea 100644 --- a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m +++ b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m @@ -137,7 +137,8 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin return NSOrderedSame; } TSMessage *message2 = (TSMessage *)object2; - return [message1 compareForSorting:message2]; + + return [@(message1.timestampForSorting) compare:@(message2.timestampForSorting)]; }]; YapDatabaseViewGrouping *grouping = [YapDatabaseViewGrouping withObjectBlock:^NSString * _Nullable(YapDatabaseReadTransaction * _Nonnull transaction, NSString * _Nonnull collection, NSString * _Nonnull key, id _Nonnull object) { @@ -163,7 +164,7 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; options.allowedCollections = [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:TSMessage.collection]]; - return [[YapDatabaseAutoView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"4" options:options]; + return [[YapDatabaseAutoView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"3" options:options]; } + (BOOL)attachmentIdShouldAppearInMediaGallery:(NSString *)attachmentId transaction:(YapDatabaseReadTransaction *)transaction diff --git a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m index 5723cf037b..b7000f0f7c 100644 --- a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m +++ b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m @@ -190,7 +190,6 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage) // // All sync registrations must be done before all async registrations, // or the sync registrations will block on the async registrations. - [TSDatabaseView asyncRegisterLegacyThreadInteractionsDatabaseView:self]; [TSDatabaseView asyncRegisterThreadInteractionsDatabaseView:self]; [TSDatabaseView asyncRegisterThreadDatabaseView:self]; [TSDatabaseView asyncRegisterUnreadDatabaseView:self]; diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.h b/SignalServiceKit/src/Storage/TSDatabaseView.h index 5fe4b15b27..3e83d174b2 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.h +++ b/SignalServiceKit/src/Storage/TSDatabaseView.h @@ -13,8 +13,6 @@ extern NSString *const TSSecondaryDevicesGroup; extern NSString *const TSThreadDatabaseViewExtensionName; extern NSString *const TSMessageDatabaseViewExtensionName; -extern NSString *const TSMessageDatabaseViewExtensionName_Legacy; - extern NSString *const TSUnreadDatabaseViewExtensionName; extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName; @@ -44,8 +42,6 @@ extern NSString *const TSLazyRestoreAttachmentsDatabaseViewExtensionName; + (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage; + (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage; -+ (void)asyncRegisterLegacyThreadInteractionsDatabaseView:(OWSStorage *)storage; - + (void)asyncRegisterThreadOutgoingMessagesDatabaseView:(OWSStorage *)storage; // Instances of OWSReadTracking for wasRead is NO and shouldAffectUnreadCounts is YES. diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.m b/SignalServiceKit/src/Storage/TSDatabaseView.m index 1d6419d6e2..ce24c10878 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.m +++ b/SignalServiceKit/src/Storage/TSDatabaseView.m @@ -24,18 +24,7 @@ NSString *const TSSecondaryDevicesGroup = @"TSSecondaryDevicesGroup"; // YAPDB BUG: when changing from non-persistent to persistent view, we had to rename TSThreadDatabaseViewExtensionName // -> TSThreadDatabaseViewExtensionName2 to work around https://github.com/yapstudios/YapDatabase/issues/324 NSString *const TSThreadDatabaseViewExtensionName = @"TSThreadDatabaseViewExtensionName2"; - -// We sort interactions by a monotonically increasing counter. -// -// Previously we sorted the interactions database by local timestamp, which was problematic if the local clock changed. -// We need to maintain the legacy extension for purposes of migration. -// -// The "Legacy" sorting extension name constant has the same value as always, so that it won't need to be rebuilt, while -// the "Modern" sorting extension name constant has the same symbol name as we've always used for sorting interactions, -// so that the callsites won't need to change. -NSString *const TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName_Monotonic"; -NSString *const TSMessageDatabaseViewExtensionName_Legacy = @"TSMessageDatabaseViewExtensionName"; - +NSString *const TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName"; NSString *const TSThreadOutgoingMessageDatabaseViewExtensionName = @"TSThreadOutgoingMessageDatabaseViewExtensionName"; NSString *const TSUnreadDatabaseViewExtensionName = @"TSUnreadDatabaseViewExtensionName"; NSString *const TSUnseenDatabaseViewExtensionName = @"TSUnseenDatabaseViewExtensionName"; @@ -111,7 +100,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"2" + version:@"1" storage:storage]; } @@ -130,7 +119,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"2" + version:@"1" storage:storage]; } @@ -158,77 +147,10 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSThreadSpecialMessagesDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"2" + version:@"1" storage:storage]; } -+ (void)asyncRegisterLegacyThreadInteractionsDatabaseView:(OWSStorage *)storage -{ - OWSAssertIsOnMainThread(); - OWSAssert(storage); - - YapDatabaseView *existingView = [storage registeredExtension:TSMessageDatabaseViewExtensionName_Legacy]; - if (existingView) { - OWSFailDebug(@"Registered database view twice: %@", TSMessageDatabaseViewExtensionName_Legacy); - return; - } - - YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( - YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) { - if (![object isKindOfClass:[TSInteraction class]]) { - OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object class], collection); - return nil; - } - TSInteraction *interaction = (TSInteraction *)object; - - return interaction.uniqueThreadId; - }]; - - YapDatabaseViewSorting *viewSorting = - [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, - NSString *group, - NSString *collection1, - NSString *key1, - id object1, - NSString *collection2, - NSString *key2, - id object2) { - if (![object1 isKindOfClass:[TSInteraction class]]) { - OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object1 class], collection1); - return NSOrderedSame; - } - if (![object2 isKindOfClass:[TSInteraction class]]) { - OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object2 class], collection2); - return NSOrderedSame; - } - TSInteraction *interaction1 = (TSInteraction *)object1; - TSInteraction *interaction2 = (TSInteraction *)object2; - - // Legit usage of timestampForLegacySorting since we're registering the - // legacy extension - uint64_t timestamp1 = interaction1.timestampForLegacySorting; - uint64_t timestamp2 = interaction2.timestampForLegacySorting; - - if (timestamp1 > timestamp2) { - return NSOrderedDescending; - } else if (timestamp1 < timestamp2) { - return NSOrderedAscending; - } else { - return NSOrderedSame; - } - }]; - - YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; - options.isPersistent = YES; - options.allowedCollections = - [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSInteraction collection]]]; - - YapDatabaseView *view = - [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"1" options:options]; - - [storage asyncRegisterExtension:view withName:TSMessageDatabaseViewExtensionName_Legacy]; -} - + (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage { YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( @@ -244,7 +166,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"2" + version:@"1" storage:storage]; } @@ -260,7 +182,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSThreadOutgoingMessageDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"3" + version:@"2" storage:storage]; } @@ -293,7 +215,13 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" } } - return [thread isArchivedWithTransaction:transaction] ? TSArchiveGroup : TSInboxGroup; + if (thread.archivalDate) { + return ([self threadShouldBeInInbox:thread]) ? TSInboxGroup : TSArchiveGroup; + } else if (thread.archivalDate) { + return TSArchiveGroup; + } else { + return TSInboxGroup; + } }]; YapDatabaseViewSorting *viewSorting = [self threadSorting]; @@ -304,11 +232,34 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSThread collection]]]; YapDatabaseView *databaseView = - [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"4" options:options]; + [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options]; [storage asyncRegisterExtension:databaseView withName:TSThreadDatabaseViewExtensionName]; } +/** + * Determines whether a thread belongs to the archive or inbox + * + * @param thread TSThread + * + * @return Inbox if true, Archive if false + */ + ++ (BOOL)threadShouldBeInInbox:(TSThread *)thread { + NSDate *lastMessageDate = thread.lastMessageDate; + NSDate *archivalDate = thread.archivalDate; + if (lastMessageDate && archivalDate) { // this is what is called + return ([lastMessageDate timeIntervalSinceDate:archivalDate] > 0) + ? YES + : NO; // if there hasn't been a new message since the archive date, it's in the archive. an issue is + // that empty threads are always given with a lastmessage date of the present on every launch + } else if (archivalDate) { + return NO; + } + + return YES; +} + + (YapDatabaseViewSorting *)threadSorting { return [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, NSString *group, @@ -329,16 +280,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" TSThread *thread1 = (TSThread *)object1; TSThread *thread2 = (TSThread *)object2; if ([group isEqualToString:TSArchiveGroup] || [group isEqualToString:TSInboxGroup]) { - - TSInteraction *_Nullable lastInteractionForInbox1 = - [thread1 lastInteractionForInboxWithTransaction:transaction]; - NSDate *date1 = lastInteractionForInbox1 ? lastInteractionForInbox1.receivedAtDate : thread1.creationDate; - - TSInteraction *_Nullable lastInteractionForInbox2 = - [thread2 lastInteractionForInboxWithTransaction:transaction]; - NSDate *date2 = lastInteractionForInbox2 ? lastInteractionForInbox2.receivedAtDate : thread2.creationDate; - - return [date1 compare:date2]; + return [thread1.lastMessageDate compare:thread2.lastMessageDate]; } return NSOrderedSame; @@ -485,7 +427,6 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" return result; } -// MJK TODO - dynamic interactions + (id)threadOutgoingMessageDatabaseView:(YapDatabaseReadTransaction *)transaction { OWSAssertDebug(transaction); diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.h b/SignalServiceKit/src/Storage/TSYapDatabaseObject.h index b8f69de094..0958def560 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.h +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.h @@ -95,7 +95,6 @@ NS_ASSUME_NONNULL_BEGIN * Assign the latest persisted values from the database. */ - (void)reload; -- (void)reloadWithTransaction:(YapDatabaseReadTransaction *)transaction; /** * Saves the object with the shared readWrite connection - does not block. diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m index 639f6760b5..d8a0eddbd3 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m @@ -218,19 +218,9 @@ NS_ASSUME_NONNULL_BEGIN } } -#pragma mark Reload - - (void)reload { - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - [self reloadWithTransaction:transaction]; - }]; -} - - -- (void)reloadWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - TSYapDatabaseObject *latest = [[self class] fetchObjectWithUniqueID:self.uniqueId transaction:transaction]; + TSYapDatabaseObject *latest = [[self class] fetchObjectWithUniqueID:self.uniqueId]; if (!latest) { OWSFailDebug(@"`latest` was unexpectedly nil"); return; diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index 312d8e8501..8e59beedcb 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2.30.1 + 2.30.2 CFBundleVersion - 2.30.1.1 + 2.30.2.4 ITSAppUsesNonExemptEncryption NSAppTransportSecurity