From 1dc9d5f86f7a97523ba0a00dc654076077bf1874 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 09:05:16 -0700 Subject: [PATCH 01/20] Fix bug where tab bar appears in archive --- .../HomeView/Chat List/ChatListViewController.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController.m b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController.m index 233db64133..f2f9b5576c 100644 --- a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController.m +++ b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController.m @@ -720,8 +720,8 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; self.isViewVisible = YES; - // Ensure the tabBar is always hidden if stories is disabled. - BOOL shouldHideTabBar = !SSKFeatureFlags.stories; + // Ensure the tabBar is always hidden if stories is disabled or we're in the archive. + BOOL shouldHideTabBar = !SSKFeatureFlags.stories || self.chatListMode == ChatListModeArchive; if (shouldHideTabBar) { self.tabBarController.tabBar.hidden = YES; self.extendedLayoutIncludesOpaqueBars = YES; From 29436f372c045d8bfaec1cb0e7a573f8f16da52c Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 09:05:28 -0700 Subject: [PATCH 02/20] Fix camera button in dark theme --- .../Stories/StoriesViewController.swift | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift index a3690eae62..5b4611de55 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift @@ -20,12 +20,6 @@ class StoriesViewController: OWSViewController { title = NSLocalizedString("STORIES_TITLE", comment: "Title for the stories view.") - let cameraButton = UIBarButtonItem(image: Theme.iconImage(.cameraButton), style: .plain, target: self, action: #selector(showCameraView)) - cameraButton.accessibilityLabel = NSLocalizedString("CAMERA_BUTTON_LABEL", comment: "Accessibility label for camera button.") - cameraButton.accessibilityHint = NSLocalizedString("CAMERA_BUTTON_HINT", comment: "Accessibility hint describing what you can do with the camera button") - - navigationItem.rightBarButtonItems = [cameraButton] - databaseStorage.appendDatabaseChangeDelegate(self) tableView.register(StoryCell.self, forCellReuseIdentifier: StoryCell.reuseIdentifier) @@ -34,6 +28,7 @@ class StoriesViewController: OWSViewController { tableView.estimatedRowHeight = 116 reloadStories() + updateNavigationBar() } private var timestampUpdateTimer: Timer? @@ -97,6 +92,16 @@ class StoriesViewController: OWSViewController { view.backgroundColor = Theme.secondaryBackgroundColor tableView.backgroundColor = Theme.secondaryBackgroundColor } + + updateNavigationBar() + } + + private func updateNavigationBar() { + let cameraButton = UIBarButtonItem(image: Theme.iconImage(.cameraButton), style: .plain, target: self, action: #selector(showCameraView)) + cameraButton.accessibilityLabel = NSLocalizedString("CAMERA_BUTTON_LABEL", comment: "Accessibility label for camera button.") + cameraButton.accessibilityHint = NSLocalizedString("CAMERA_BUTTON_HINT", comment: "Accessibility hint describing what you can do with the camera button") + + navigationItem.rightBarButtonItems = [cameraButton] } @objc From e95e39249405f58f4a4343fdb63a19c64e5304e9 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 12:21:03 -0700 Subject: [PATCH 03/20] Add story context to TSInteraction --- .../sds_codegen/sds_config/sds-config.json | 3 +- SignalServiceKit/Resources/schema.sql | 67 ++++++++-------- SignalServiceKit/src/Contacts/TSThread.m | 6 +- .../src/Messages/Interactions/TSMessage.h | 9 ++- .../src/Messages/Interactions/TSMessage.m | 14 +++- .../Storage/Database/GRDBSchemaMigrator.swift | 34 ++++++++- .../Database/Records/InteractionFinder.swift | 76 +++++++++++++------ 7 files changed, 151 insertions(+), 58 deletions(-) diff --git a/Scripts/sds_codegen/sds_config/sds-config.json b/Scripts/sds_codegen/sds_config/sds-config.json index 2440c763e0..5d5b31fb94 100644 --- a/Scripts/sds_codegen/sds_config/sds-config.json +++ b/Scripts/sds_codegen/sds_config/sds-config.json @@ -13,7 +13,8 @@ "OWSBackupFragment.uncompressedDataLength": "UInt64", "TestModel.nsNumberValueUsingInt64": "Int64", "TestModel.nsNumberValueUsingUInt64": "UInt64", - "SSKJobRecord.exclusiveProcessIdentifier": "Int32" + "SSKJobRecord.exclusiveProcessIdentifier": "Int32", + "TSMessage.storyTimestamp": "UInt64" }, "properties_to_ignore": [ "TSYapDatabaseObject.grdbId", diff --git a/SignalServiceKit/Resources/schema.sql b/SignalServiceKit/Resources/schema.sql index 383a6fc8be..2c3a8451c7 100644 --- a/SignalServiceKit/Resources/schema.sql +++ b/SignalServiceKit/Resources/schema.sql @@ -115,6 +115,9 @@ CREATE ,"paymentRequest" BLOB ,"viewed" BOOLEAN ,"serverGuid" TEXT + ,"storyAuthorUuidString" TEXT + ,"storyTimestamp" INTEGER + ,"isGroupStoryReply" BOOLEAN ) ; @@ -533,14 +536,6 @@ CREATE ) ; -CREATE - INDEX "index_interactions_unread_counts" - ON "model_TSInteraction"("read" - ,"uniqueThreadId" - ,"recordType" -) -; - CREATE INDEX "index_interactions_on_expiresInSeconds_and_expiresAt" ON "model_TSInteraction"("expiresAt" @@ -1220,28 +1215,6 @@ CREATE ) ; -CREATE - INDEX index_model_TSInteraction_ConversationLoadInteractionCount - ON model_TSInteraction ( - uniqueThreadId - ,recordType - ) -WHERE - recordType IS NOT 70 -; - -CREATE - INDEX index_model_TSInteraction_ConversationLoadInteractionDistance - ON model_TSInteraction ( - uniqueThreadId - ,id - ,recordType - ,uniqueId - ) -WHERE - recordType IS NOT 70 -; - CREATE TABLE IF NOT EXISTS "model_ProfileBadgeTable" ( @@ -1299,3 +1272,37 @@ CREATE ) ) ; + +CREATE + INDEX index_model_TSInteraction_ConversationLoadInteractionCount + ON model_TSInteraction ( + uniqueThreadId + ,isGroupStoryReply + ,recordType + ) +WHERE + recordType IS NOT 70 +; + +CREATE + INDEX index_model_TSInteraction_ConversationLoadInteractionDistance + ON model_TSInteraction ( + uniqueThreadId + ,id + ,isGroupStoryReply + ,recordType + ,uniqueId + ) +WHERE + recordType IS NOT 70 +; + +CREATE + INDEX index_model_TSInteraction_UnreadCount + ON model_TSInteraction ( + READ + ,isGroupStoryReply + ,uniqueThreadId + ,recordType + ) +; diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index a6ad42a48c..806a873a47 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -357,8 +357,10 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer - (NSUInteger)numberOfInteractionsWithTransaction:(SDSAnyReadTransaction *)transaction { OWSAssertDebug(transaction); - return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] countExcludingPlaceholders:NO - transaction:transaction]; + return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] + countExcludingPlaceholders:NO + storyReplyQueryMode:StoryReplyQueryModeInclude + transaction:transaction]; } - (nullable TSInteraction *)lastInteractionForInboxWithTransaction:(SDSAnyReadTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index b7ef385759..f5b9e82c33 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -46,6 +46,13 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL isViewOnceComplete; @property (nonatomic, readonly) BOOL wasRemotelyDeleted; +// Story Context +@property (nonatomic, readonly, nullable) NSNumber *storyTimestamp; +@property (nonatomic, readonly, nullable) SignalServiceAddress *storyAuthorAddress; +@property (nonatomic, readonly, nullable) NSString *storyAuthorUuidString; +@property (nonatomic, readonly) BOOL isGroupStoryReply; +@property (nonatomic, readonly) BOOL isStoryReply; + - (instancetype)initWithUniqueId:(NSString *)uniqueId timestamp:(uint64_t)timestamp thread:(TSThread *)thread NS_UNAVAILABLE; diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 8288b40a62..c2b37bb3c2 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSMessage.h" @@ -298,6 +298,18 @@ static const NSUInteger OWSMessageSchemaVersion = 4; } } +#pragma mark - Story Context + +- (nullable SignalServiceAddress *)storyAuthorAddress +{ + return [[SignalServiceAddress alloc] initWithUuidString:self.storyAuthorUuidString]; +} + +- (BOOL)isStoryReply +{ + return self.storyAuthorUuidString != nil && self.storyTimestamp != nil; +} + #pragma mark - Attachments - (BOOL)hasAttachments diff --git a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift index c0c6716c07..5eeab7179d 100644 --- a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift +++ b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift @@ -124,6 +124,7 @@ public class GRDBSchemaMigrator: NSObject { case createSubscriptionDurableJob case addReceiptPresentationToSubscriptionDurableJob case createStoryMessageTable + case addColumnsForStoryContext // NOTE: Every time we add a migration id, consider // incrementing grdbSchemaVersionLatest. @@ -166,7 +167,7 @@ public class GRDBSchemaMigrator: NSObject { } public static let grdbSchemaVersionDefault: UInt = 0 - public static let grdbSchemaVersionLatest: UInt = 33 + public static let grdbSchemaVersionLatest: UInt = 34 // An optimization for new users, we have the first migration import the latest schema // and mark any other migrations as "already run". @@ -1565,6 +1566,37 @@ public class GRDBSchemaMigrator: NSObject { } } + migrator.registerMigration(MigrationId.addColumnsForStoryContext.rawValue) { db in + do { + try db.alter(table: "model_TSInteraction") { (table: TableAlteration) -> Void in + table.add(column: "storyAuthorUuidString", .text) + table.add(column: "storyTimestamp", .integer) + table.add(column: "isGroupStoryReply", .boolean) + } + + try db.execute(sql: "UPDATE model_TSInteraction SET isGroupStoryReply = 0") + + try db.execute(sql: """ + DROP INDEX index_model_TSInteraction_ConversationLoadInteractionCount; + DROP INDEX index_model_TSInteraction_ConversationLoadInteractionDistance; + DROP INDEX index_interactions_unread_counts; + + CREATE INDEX index_model_TSInteraction_ConversationLoadInteractionCount + ON model_TSInteraction(uniqueThreadId, isGroupStoryReply, recordType) + WHERE recordType IS NOT \(SDSRecordType.recoverableDecryptionPlaceholder.rawValue); + + CREATE INDEX index_model_TSInteraction_ConversationLoadInteractionDistance + ON model_TSInteraction(uniqueThreadId, id, isGroupStoryReply, recordType, uniqueId) + WHERE recordType IS NOT \(SDSRecordType.recoverableDecryptionPlaceholder.rawValue); + + CREATE INDEX index_model_TSInteraction_UnreadCount + ON model_TSInteraction(read, isGroupStoryReply, uniqueThreadId, recordType); + """) + } catch { + owsFail("Error: \(error)") + } + } + // MARK: - Schema Migration Insertion Point } diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index 391cd76e09..697e3f619a 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -5,6 +5,13 @@ import Foundation import GRDB +@objc +public enum StoryReplyQueryMode: UInt { + case include = 0 + case excludeGroupReplies = 1 + case onlyGroupReplies = 2 +} + protocol InteractionFinderAdapter { associatedtype ReadTransaction @@ -39,12 +46,12 @@ protocol InteractionFinderAdapter { func earliestKnownInteractionRowId(transaction: ReadTransaction) -> Int? - func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool, transaction: ReadTransaction) throws -> UInt? - func count(excludingPlaceholders excludePlaceholders: Bool, transaction: ReadTransaction) -> UInt + func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool, storyReplyQueryMode: StoryReplyQueryMode, transaction: ReadTransaction) throws -> UInt? + func count(excludingPlaceholders excludePlaceholders: Bool, storyReplyQueryMode: StoryReplyQueryMode, transaction: ReadTransaction) -> UInt func enumerateInteractionIds(transaction: ReadTransaction, block: @escaping (String, UnsafeMutablePointer) throws -> Void) throws func enumerateRecentInteractions(transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws - func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool, transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws - func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool, transaction: ReadTransaction) throws -> [String] + func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool, storyReplyQueryMode: StoryReplyQueryMode, transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws + func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool, storyReplyQueryMode: StoryReplyQueryMode, transaction: ReadTransaction) throws -> [String] func existsOutgoingMessage(transaction: ReadTransaction) -> Bool func outgoingMessageCount(transaction: ReadTransaction) -> UInt @@ -145,6 +152,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } unreadInteractionQuery += " WHERE \(sqlClauseForUnreadInteractionCounts(interactionsAlias: "interaction")) " + unreadInteractionQuery += " \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies, interactionsAlias: "interaction")) " guard let unreadInteractionCount = try UInt.fetchOne(transaction.database, sql: unreadInteractionQuery) else { owsFailDebug("unreadInteractionCount was unexpectedly nil") @@ -287,21 +295,21 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - public func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool = true, transaction: SDSAnyReadTransaction) throws -> UInt? { - return try Bench(title: "InteractionFinder.distanceFromLatestExcludingPlaceholders_\(excludePlaceholders)") { + public func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: SDSAnyReadTransaction) throws -> UInt? { + return try Bench(title: "InteractionFinder.distanceFromLatestExcludingPlaceholders_\(excludePlaceholders)_StoryReplyQueryMode_\(storyReplyQueryMode)") { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.distanceFromLatest(interactionUniqueId: interactionUniqueId, excludingPlaceholders: excludePlaceholders, transaction: grdbRead) + return try grdbAdapter.distanceFromLatest(interactionUniqueId: interactionUniqueId, excludingPlaceholders: excludePlaceholders, storyReplyQueryMode: storyReplyQueryMode, transaction: grdbRead) } } } - @objc(countExcludingPlaceholders:transaction:) - public func count(excludingPlaceholders excludePlaceholders: Bool = true, transaction: SDSAnyReadTransaction) -> UInt { - return Bench(title: "InteractionFinder.countExcludingPlaceholders_\(excludePlaceholders)") { + @objc(countExcludingPlaceholders:storyReplyQueryMode:transaction:) + public func count(excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: SDSAnyReadTransaction) -> UInt { + return Bench(title: "InteractionFinder.countExcludingPlaceholders_\(excludePlaceholders)_StoryReplyQueryMode_\(storyReplyQueryMode)") { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return grdbAdapter.count(excludingPlaceholders: excludePlaceholders, transaction: grdbRead) + return grdbAdapter.count(excludingPlaceholders: excludePlaceholders, storyReplyQueryMode: storyReplyQueryMode, transaction: grdbRead) } } } @@ -353,20 +361,20 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - public func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, transaction: SDSAnyReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { - return try Bench(title: "InteractionFinder.enumerateInteractionsInRangeExcludingPlaceholders_\(excludePlaceholders)") { + public func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: SDSAnyReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { + return try Bench(title: "InteractionFinder.enumerateInteractionsInRangeExcludingPlaceholders_\(excludePlaceholders)_StoryReplyQueryMode_\(storyReplyQueryMode)") { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.enumerateInteractions(range: range, excludingPlaceholders: excludePlaceholders, transaction: grdbRead, block: block) + return try grdbAdapter.enumerateInteractions(range: range, excludingPlaceholders: excludePlaceholders, storyReplyQueryMode: storyReplyQueryMode, transaction: grdbRead, block: block) } } } - public func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, transaction: SDSAnyReadTransaction) throws -> [String] { - return try Bench(title: "InteractionFinder.interactionsIdsInRangeExcludingPlaceholders_\(excludePlaceholders)") { + public func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: SDSAnyReadTransaction) throws -> [String] { + return try Bench(title: "InteractionFinder.interactionsIdsInRangeExcludingPlaceholders_\(excludePlaceholders)_StoryReplyQueryMode_\(storyReplyQueryMode)") { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.interactionIds(inRange: range, excludingPlaceholders: excludePlaceholders, transaction: grdbRead) + return try grdbAdapter.interactionIds(inRange: range, excludingPlaceholders: excludePlaceholders, storyReplyQueryMode: storyReplyQueryMode, transaction: grdbRead) } } } @@ -380,6 +388,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(sqlClauseForAllUnreadInteractions) + \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies)) ORDER BY \(interactionColumn: .id) """ @@ -398,7 +407,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } @objc - public func countUnreadMessages(beforeSortId: UInt64, transaction: GRDBReadTransaction) -> UInt { + public func countUnreadMessages(beforeSortId: UInt64, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction) -> UInt { do { let sql = """ SELECT COUNT(*) @@ -406,6 +415,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) <= ? AND \(sqlClauseForAllUnreadInteractions) + \(GRDBInteractionFinder.filterStoryRepliesClause(for: storyReplyQueryMode)) """ guard let count = try UInt.fetchOne(transaction.database, @@ -1047,7 +1057,25 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { // If you need to adjust this clause, you should probably update the index as well. This is a perf sensitive code path. private let filterPlaceholdersClause = "AND \(interactionColumn: .recordType) IS NOT \(SDSRecordType.recoverableDecryptionPlaceholder.rawValue)" - func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool = true, transaction: GRDBReadTransaction) throws -> UInt? { + fileprivate static func filterStoryRepliesClause(for queryMode: StoryReplyQueryMode, interactionsAlias: String? = nil) -> String { + let columnPrefix: String + if let interactionsAlias = interactionsAlias { + columnPrefix = interactionsAlias + "." + } else { + columnPrefix = "" + } + + switch queryMode { + case .excludeGroupReplies: + return "AND \(columnPrefix)\(interactionColumn: .isGroupStoryReply) = 0" + case .onlyGroupReplies: + return "AND \(columnPrefix)\(interactionColumn: .isGroupStoryReply) = 1" + case .include: + return "" + } + } + + func distanceFromLatest(interactionUniqueId: String, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction) throws -> UInt? { let fetchInteractionIdSQL = """ SELECT id @@ -1069,6 +1097,7 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) >= ? + \(Self.filterStoryRepliesClause(for: storyReplyQueryMode)) \(excludePlaceholders ? filterPlaceholdersClause : "") """ let distanceArguments: StatementArguments = [threadUniqueId, interactionId] @@ -1084,12 +1113,13 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return distanceFromLatest } - func count(excludingPlaceholders excludePlaceholders: Bool = true, transaction: GRDBReadTransaction) -> UInt { + func count(excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction) -> UInt { do { let sql: String = """ SELECT COUNT(*) FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(Self.filterStoryRepliesClause(for: storyReplyQueryMode)) \(excludePlaceholders ? filterPlaceholdersClause : "") """ let arguments: StatementArguments = [threadUniqueId] @@ -1142,11 +1172,12 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } - func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, transaction: GRDBReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { + func enumerateInteractions(range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { let sql = """ SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(Self.filterStoryRepliesClause(for: storyReplyQueryMode)) \(excludePlaceholders ? filterPlaceholdersClause : "") ORDER BY \(interactionColumn: .id) LIMIT \(range.length) @@ -1166,11 +1197,12 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } - func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, transaction: GRDBReadTransaction) throws -> [String] { + func interactionIds(inRange range: NSRange, excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction) throws -> [String] { let sql = """ SELECT \(interactionColumn: .uniqueId) FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(Self.filterStoryRepliesClause(for: storyReplyQueryMode)) \(excludePlaceholders ? filterPlaceholdersClause : "") ORDER BY \(interactionColumn: .id) LIMIT \(range.length) From 536c7170df9a5ce7e84ee7709003c08976c74cd7 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 12:22:37 -0700 Subject: [PATCH 04/20] sds codegen --- .../sds_config/sds-property_order.json | 3 + ...ngConfigurationUpdateInfoMessage+SDS.swift | 5 +- ...sappearingConfigurationUpdateInfoMessage.h | 7 +- ...sappearingConfigurationUpdateInfoMessage.m | 8 +- ...WSVerificationStateChangeMessage+SDS.swift | 5 +- .../OWSVerificationStateChangeMessage.h | 7 +- .../OWSVerificationStateChangeMessage.m | 8 +- .../Interactions/TSErrorMessage+SDS.swift | 5 +- .../Messages/Interactions/TSErrorMessage.h | 7 +- .../Messages/Interactions/TSErrorMessage.m | 8 +- .../Interactions/TSIncomingMessage+SDS.swift | 5 +- .../Messages/Interactions/TSIncomingMessage.h | 7 +- .../Messages/Interactions/TSIncomingMessage.m | 8 +- .../Interactions/TSInfoMessage+SDS.swift | 5 +- .../src/Messages/Interactions/TSInfoMessage.h | 7 +- .../src/Messages/Interactions/TSInfoMessage.m | 8 +- .../Interactions/TSInteraction+SDS.swift | 214 +++++++++++++++++- .../Messages/Interactions/TSMessage+SDS.swift | 5 +- .../src/Messages/Interactions/TSMessage.h | 5 +- .../src/Messages/Interactions/TSMessage.m | 6 + .../Interactions/TSOutgoingMessage+SDS.swift | 5 +- .../Messages/Interactions/TSOutgoingMessage.h | 5 +- .../Messages/Interactions/TSOutgoingMessage.m | 6 + .../TSUnreadIndicatorInteraction+SDS.swift | 5 +- ...TSInvalidIdentityKeyErrorMessage+SDS.swift | 5 +- ...IdentityKeyReceivingErrorMessage+SDS.swift | 5 +- ...SInvalidIdentityKeyReceivingErrorMessage.h | 7 +- ...SInvalidIdentityKeyReceivingErrorMessage.m | 8 +- ...idIdentityKeySendingErrorMessage+SDS.swift | 5 +- .../TSInvalidIdentityKeySendingErrorMessage.h | 7 +- .../TSInvalidIdentityKeySendingErrorMessage.m | 8 +- .../OWSAddToContactsOfferMessage+SDS.swift | 5 +- ...ddToProfileWhitelistOfferMessage+SDS.swift | 5 +- .../Messages/OWSGroupCallMessage+SDS.swift | 5 +- ...RecoverableDecryptionPlaceholder+SDS.swift | 5 +- ...SUnknownContactBlockOfferMessage+SDS.swift | 5 +- ...OWSUnknownProtocolVersionMessage+SDS.swift | 5 +- .../OWSUnknownProtocolVersionMessage.h | 7 +- .../OWSUnknownProtocolVersionMessage.m | 8 +- .../OWSOutgoingPaymentMessage+SDS.swift | 5 +- .../Payments/OWSOutgoingPaymentMessage.h | 7 +- .../Payments/OWSOutgoingPaymentMessage.m | 8 +- .../src/Messages/TSCall+SDS.swift | 5 +- 43 files changed, 419 insertions(+), 50 deletions(-) diff --git a/Scripts/sds_codegen/sds_config/sds-property_order.json b/Scripts/sds_codegen/sds_config/sds-property_order.json index 18980983fe..4ef3ec762c 100644 --- a/Scripts/sds_codegen/sds_config/sds-property_order.json +++ b/Scripts/sds_codegen/sds_config/sds-property_order.json @@ -83,6 +83,7 @@ "InteractionRecord.hasSyncedTranscript": 23, "InteractionRecord.infoMessageUserInfo": 50, "InteractionRecord.isFromLinkedDevice": 24, + "InteractionRecord.isGroupStoryReply": 67, "InteractionRecord.isLocalChange": 25, "InteractionRecord.isViewOnceComplete": 26, "InteractionRecord.isViewOnceMessage": 27, @@ -113,6 +114,8 @@ "InteractionRecord.sourceDeviceId": 44, "InteractionRecord.storedMessageState": 45, "InteractionRecord.storedShouldStartExpireTimer": 46, + "InteractionRecord.storyAuthorUuidString": 65, + "InteractionRecord.storyTimestamp": 66, "InteractionRecord.timestamp": 2, "InteractionRecord.uniqueThreadId": 3, "InteractionRecord.unregisteredAddress": 47, diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift index afadefffd5..cd84b929d7 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSDisappearingConfigurationUpdateInfoMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h index 50d39285a9..137fef4b1a 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -67,12 +67,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -83,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN configurationIsEnabled:(BOOL)configurationIsEnabled createdByRemoteName:(nullable NSString *)createdByRemoteName createdInExistingGroup:(BOOL)createdInExistingGroup -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:configurationDurationSeconds:configurationIsEnabled:createdByRemoteName:createdInExistingGroup:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:configurationDurationSeconds:configurationIsEnabled:createdByRemoteName:createdInExistingGroup:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index 737c2c7915..77623fecef 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "OWSDisappearingConfigurationUpdateInfoMessage.h" @@ -67,12 +67,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -97,12 +100,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage infoMessageUserInfo:infoMessageUserInfo diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift index 9b0d914852..927cd3d6ea 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSVerificationStateChangeMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h index 3b2e9e6e58..44981a7b42 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -67,12 +67,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -82,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN isLocalChange:(BOOL)isLocalChange recipientAddress:(SignalServiceAddress *)recipientAddress verificationState:(OWSVerificationState)verificationState -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:isLocalChange:recipientAddress:verificationState:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:isLocalChange:recipientAddress:verificationState:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m index 145fa911a6..bedbb4c48c 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "OWSVerificationStateChangeMessage.h" @@ -61,12 +61,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -90,12 +93,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage infoMessageUserInfo:infoMessageUserInfo diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift index ff289e9282..46ba7a68b2 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift @@ -125,7 +125,10 @@ class TSErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index 628c2dab79..8518467739 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -88,19 +88,22 @@ extern NSUInteger TSErrorMessageSchemaVersion; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read recipientAddress:(nullable SignalServiceAddress *)recipientAddress sender:(nullable SignalServiceAddress *)sender wasIdentityVerified:(BOOL)wasIdentityVerified -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 690b1a98b4..8141b188ff 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSErrorMessage.h" @@ -136,12 +136,15 @@ NSUInteger TSErrorMessageSchemaVersion = 2; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read @@ -162,12 +165,15 @@ NSUInteger TSErrorMessageSchemaVersion = 2; expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; if (!self) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift index 26bfea037b..082ee62e3f 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift @@ -125,7 +125,10 @@ class TSIncomingMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = model.wasViewed let serverGuid: String? = model.serverGuid + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h index a3e261d3d6..ca49d2cdab 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -67,12 +67,15 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(incomingMessageWithBuilder:)); expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted authorPhoneNumber:(nullable NSString *)authorPhoneNumber authorUUID:(nullable NSString *)authorUUID @@ -83,7 +86,7 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(incomingMessageWithBuilder:)); sourceDeviceId:(unsigned int)sourceDeviceId viewed:(BOOL)viewed wasReceivedByUD:(BOOL)wasReceivedByUD -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:authorPhoneNumber:authorUUID:read:serverDeliveryTimestamp:serverGuid:serverTimestamp:sourceDeviceId:viewed:wasReceivedByUD:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:authorPhoneNumber:authorUUID:read:serverDeliveryTimestamp:serverGuid:serverTimestamp:sourceDeviceId:viewed:wasReceivedByUD:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m index 1bfc0a7f61..63d98a3dda 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSIncomingMessage.h" @@ -91,12 +91,15 @@ const NSUInteger TSIncomingMessageSchemaVersion = 1; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted authorPhoneNumber:(nullable NSString *)authorPhoneNumber authorUUID:(nullable NSString *)authorUUID @@ -121,12 +124,15 @@ const NSUInteger TSIncomingMessageSchemaVersion = 1; expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; if (!self) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift index d4f2a4110e..b2592f5ca5 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift @@ -125,7 +125,10 @@ class TSInfoMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h index 2bca16bb98..5c8520ad39 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -109,19 +109,22 @@ extern InfoMessageUserInfoKey const InfoMessageUserInfoKeyChangePhoneNumberNew; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo messageType:(TSInfoMessageType)messageType read:(BOOL)read unregisteredAddress:(nullable SignalServiceAddress *)unregisteredAddress -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m index 568382438f..dacfdbd33a 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSInfoMessage.h" @@ -141,12 +141,15 @@ NSUInteger TSInfoMessageSchemaVersion = 2; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -167,12 +170,15 @@ NSUInteger TSInfoMessageSchemaVersion = 2; expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; if (!self) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift index 7f904edec9..0d87366779 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift @@ -94,6 +94,9 @@ public struct InteractionRecord: SDSRecord { public let paymentRequest: Data? public let viewed: Bool? public let serverGuid: String? + public let storyAuthorUuidString: String? + public let storyTimestamp: UInt64? + public let isGroupStoryReply: Bool? public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { case id @@ -163,6 +166,9 @@ public struct InteractionRecord: SDSRecord { case paymentRequest case viewed case serverGuid + case storyAuthorUuidString + case storyTimestamp + case isGroupStoryReply } public static func columnName(_ column: InteractionRecord.CodingKeys, fullyQualified: Bool = false) -> String { @@ -253,6 +259,9 @@ public extension InteractionRecord { paymentRequest = row[64] viewed = row[65] serverGuid = row[66] + storyAuthorUuidString = row[67] + storyTimestamp = row[68] + isGroupStoryReply = row[69] } } @@ -298,6 +307,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -307,6 +317,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -331,12 +343,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -361,6 +376,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -370,6 +386,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -394,12 +412,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -424,6 +445,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -433,6 +455,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -461,12 +485,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -521,6 +548,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -530,6 +558,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else { @@ -569,12 +599,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, groupMetaMessage: groupMetaMessage, @@ -608,6 +641,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -617,6 +651,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -641,12 +677,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -671,6 +710,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -680,6 +720,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -704,12 +746,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -734,6 +779,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -743,6 +789,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -770,12 +818,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -802,6 +853,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -811,6 +863,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -841,12 +895,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -899,6 +956,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -908,6 +966,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -932,12 +992,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -962,6 +1025,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -971,6 +1035,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let authorPhoneNumber: String? = record.authorPhoneNumber let authorUUID: String? = record.authorUUID @@ -995,12 +1061,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, @@ -1029,6 +1098,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1038,6 +1108,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo @@ -1062,12 +1134,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -1107,6 +1182,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1116,6 +1192,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -1140,12 +1218,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -1170,6 +1251,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1179,6 +1261,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -1205,12 +1289,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -1237,6 +1324,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1246,6 +1334,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { throw SDSError.missingRequiredField @@ -1273,12 +1363,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -1305,6 +1398,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1314,6 +1408,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") return TSMessage(grdbId: recordId, @@ -1329,12 +1425,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted) case .outgoingMessage: @@ -1354,6 +1453,7 @@ extension TSInteraction { let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt") let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt") let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds") + let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply") let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete") let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage") let linkPreviewSerialized: Data? = record.linkPreview @@ -1363,6 +1463,8 @@ extension TSInteraction { let quotedMessageSerialized: Data? = record.quotedMessage let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") + let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else { @@ -1396,12 +1498,15 @@ extension TSInteraction { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, groupMetaMessage: groupMetaMessage, @@ -1588,6 +1693,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -1630,6 +1736,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage @@ -1707,12 +1815,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, groupMetaMessage: groupMetaMessage, @@ -1770,6 +1881,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -1812,6 +1924,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage @@ -1850,12 +1964,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, groupMetaMessage: groupMetaMessage, @@ -1910,6 +2027,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -1952,6 +2070,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2001,12 +2121,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2058,6 +2181,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2100,6 +2224,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2158,12 +2284,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2214,6 +2343,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2256,6 +2386,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2304,12 +2436,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2362,6 +2497,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2404,6 +2540,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2448,12 +2586,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2502,6 +2643,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2544,6 +2686,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2588,12 +2732,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2642,6 +2789,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2684,6 +2832,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage // NOTE: If this generates build errors, you made need to @@ -2728,12 +2878,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, infoMessageUserInfo: infoMessageUserInfo, @@ -2782,6 +2935,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2824,6 +2978,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let authorPhoneNumber: String? = modelToCopy.authorPhoneNumber let authorUUID: String? = modelToCopy.authorUUID @@ -2848,12 +3004,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, @@ -2906,6 +3065,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -2948,6 +3108,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -2996,12 +3158,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3052,6 +3217,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3094,6 +3260,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -3140,12 +3308,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3196,6 +3367,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3238,6 +3410,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -3282,12 +3456,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3336,6 +3513,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3378,6 +3556,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -3422,12 +3602,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3476,6 +3659,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3518,6 +3702,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -3562,12 +3748,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3616,6 +3805,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3658,6 +3848,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType let read: Bool = modelToCopy.wasRead @@ -3702,12 +3894,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, read: read, @@ -3756,6 +3951,7 @@ extension TSInteraction: DeepCopyable { let expireStartedAt: UInt64 = modelToCopy.expireStartedAt let expiresAt: UInt64 = modelToCopy.expiresAt let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds + let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage // NOTE: If this generates build errors, you made need to @@ -3798,6 +3994,8 @@ extension TSInteraction: DeepCopyable { quotedMessage = nil } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer + let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted return TSMessage(grdbId: id, @@ -3813,12 +4011,15 @@ extension TSInteraction: DeepCopyable { expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, + isGroupStoryReply: isGroupStoryReply, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, linkPreview: linkPreview, messageSticker: messageSticker, quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, + storyAuthorUuidString: storyAuthorUuidString, + storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted) } @@ -3976,6 +4177,9 @@ extension TSInteractionSerializer { static var paymentRequestColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "paymentRequest", columnType: .blob, isOptional: true) } static var viewedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "viewed", columnType: .int, isOptional: true) } static var serverGuidColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "serverGuid", columnType: .unicodeString, isOptional: true) } + static var storyAuthorUuidStringColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyAuthorUuidString", columnType: .unicodeString, isOptional: true) } + static var storyTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyTimestamp", columnType: .int64, isOptional: true) } + static var isGroupStoryReplyColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isGroupStoryReply", columnType: .int, isOptional: true) } // TODO: We should decide on a naming convention for // tables that store models. @@ -4049,7 +4253,10 @@ extension TSInteractionSerializer { paymentNotificationColumn, paymentRequestColumn, viewedColumn, - serverGuidColumn + serverGuidColumn, + storyAuthorUuidStringColumn, + storyTimestampColumn, + isGroupStoryReplyColumn ]) } } @@ -4520,8 +4727,11 @@ class TSInteractionSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = nil + let storyTimestamp: UInt64? = nil + let isGroupStoryReply: Bool? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift index 400a944b1e..2474faa20b 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift @@ -125,7 +125,10 @@ class TSMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index f5b9e82c33..3b91040d28 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -92,14 +92,17 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(messageWithBuilder:)); expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index c2b37bb3c2..1752a0148b 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -119,12 +119,15 @@ static const NSUInteger OWSMessageSchemaVersion = 4; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted { self = [super initWithGrdbId:grdbId @@ -145,12 +148,15 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _expireStartedAt = expireStartedAt; _expiresAt = expiresAt; _expiresInSeconds = expiresInSeconds; + _isGroupStoryReply = isGroupStoryReply; _isViewOnceComplete = isViewOnceComplete; _isViewOnceMessage = isViewOnceMessage; _linkPreview = linkPreview; _messageSticker = messageSticker; _quotedMessage = quotedMessage; _storedShouldStartExpireTimer = storedShouldStartExpireTimer; + _storyAuthorUuidString = storyAuthorUuidString; + _storyTimestamp = storyTimestamp; _wasRemotelyDeleted = wasRemotelyDeleted; [self sdsFinalizeMessage]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift index 142ba53976..992f4cd459 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift @@ -125,7 +125,10 @@ class TSOutgoingMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index 3cc56f2ffe..90d5a386de 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -131,12 +131,15 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage @@ -149,7 +152,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { mostRecentFailureText:(nullable NSString *)mostRecentFailureText recipientAddressStates:(nullable NSDictionary *)recipientAddressStates storedMessageState:(TSOutgoingMessageState)storedMessageState -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index b03a73e354..bc8257ab8e 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -135,12 +135,15 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage @@ -167,12 +170,15 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; if (!self) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift index 65b2264a7a..6bc15afa44 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift @@ -125,7 +125,10 @@ class TSUnreadIndicatorInteractionSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = nil + let storyTimestamp: UInt64? = nil + let isGroupStoryReply: Bool? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift index 2075833973..94e61d8bef 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift @@ -125,7 +125,10 @@ class TSInvalidIdentityKeyErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift index c5f0b56caf..185aab21fa 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift @@ -125,7 +125,10 @@ class TSInvalidIdentityKeyReceivingErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h index 9986f0874c..0988e543ba 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -38,12 +38,15 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read @@ -52,7 +55,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage wasIdentityVerified:(BOOL)wasIdentityVerified authorId:(NSString *)authorId envelopeData:(nullable NSData *)envelopeData -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:authorId:envelopeData:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:authorId:envelopeData:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 2c9ab25b9a..20be866953 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSInvalidIdentityKeyReceivingErrorMessage.h" @@ -99,12 +99,15 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read @@ -127,12 +130,15 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted errorType:errorType read:read diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift index 32ac9ab5f4..0307bf616d 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift @@ -125,7 +125,10 @@ class TSInvalidIdentityKeySendingErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h index effae9d7c9..76eaa5d30e 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -37,12 +37,15 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read @@ -51,7 +54,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : wasIdentityVerified:(BOOL)wasIdentityVerified messageId:(NSString *)messageId preKeyBundle:(PreKeyBundle *)preKeyBundle -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:messageId:preKeyBundle:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:messageId:preKeyBundle:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m index 00a66b3ef7..f6a1a676cb 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "TSInvalidIdentityKeySendingErrorMessage.h" @@ -52,12 +52,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType read:(BOOL)read @@ -80,12 +83,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted errorType:errorType read:read diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift index 168ca7366f..f32e03036e 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSAddToContactsOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift index 9a58a4abdb..3314261cee 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSAddToProfileWhitelistOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift index 3525e0fd18..e2c86ba527 100644 --- a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSGroupCallMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = nil + let storyTimestamp: UInt64? = nil + let isGroupStoryReply: Bool? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift index 0bf2e103fb..9119fa237b 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift @@ -125,7 +125,10 @@ class OWSRecoverableDecryptionPlaceholderSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift index c70e26331e..e2f23837d7 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSUnknownContactBlockOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift index 50271fdf7c..be6d553ae6 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSUnknownProtocolVersionMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h index 6cea14ee5d..fd48d188ff 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -66,12 +66,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -80,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN unregisteredAddress:(nullable SignalServiceAddress *)unregisteredAddress protocolVersion:(NSUInteger)protocolVersion sender:(nullable SignalServiceAddress *)sender -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:protocolVersion:sender:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:protocolVersion:sender:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m index a02c70715e..c90f9088ac 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "OWSUnknownProtocolVersionMessage.h" @@ -78,12 +78,15 @@ NSUInteger const OWSUnknownProtocolVersionMessageSchemaVersion = 1; expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage infoMessageUserInfo:(nullable NSDictionary *)infoMessageUserInfo @@ -106,12 +109,15 @@ NSUInteger const OWSUnknownProtocolVersionMessageSchemaVersion = 1; expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage infoMessageUserInfo:infoMessageUserInfo diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift index 0058e550ca..5b36d041f5 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift @@ -125,7 +125,10 @@ class OWSOutgoingPaymentMessageSerializer: SDSSerializer { let paymentRequest: Data? = optionalArchive(model.paymentRequest) let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = model.storyAuthorUuidString + let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) + let isGroupStoryReply: Bool? = model.isGroupStoryReply - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h index bb000b8bcc..3389695783 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -43,12 +43,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage @@ -64,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN paymentCancellation:(nullable TSPaymentCancellation *)paymentCancellation paymentNotification:(nullable TSPaymentNotification *)paymentNotification paymentRequest:(nullable TSPaymentRequest *)paymentRequest -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:paymentCancellation:paymentNotification:paymentRequest:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:paymentCancellation:paymentNotification:paymentRequest:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m index 9fd5a1ecea..f10cbfe81f 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "OWSOutgoingPaymentMessage.h" @@ -57,12 +57,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:(uint64_t)expireStartedAt expiresAt:(uint64_t)expiresAt expiresInSeconds:(unsigned int)expiresInSeconds + isGroupStoryReply:(BOOL)isGroupStoryReply isViewOnceComplete:(BOOL)isViewOnceComplete isViewOnceMessage:(BOOL)isViewOnceMessage linkPreview:(nullable OWSLinkPreview *)linkPreview messageSticker:(nullable MessageSticker *)messageSticker quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage @@ -92,12 +95,15 @@ NS_ASSUME_NONNULL_BEGIN expireStartedAt:expireStartedAt expiresAt:expiresAt expiresInSeconds:expiresInSeconds + isGroupStoryReply:isGroupStoryReply isViewOnceComplete:isViewOnceComplete isViewOnceMessage:isViewOnceMessage linkPreview:linkPreview messageSticker:messageSticker quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer + storyAuthorUuidString:storyAuthorUuidString + storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage groupMetaMessage:groupMetaMessage diff --git a/SignalServiceKit/src/Messages/TSCall+SDS.swift b/SignalServiceKit/src/Messages/TSCall+SDS.swift index 334cfc0cd8..4a3fc2092c 100644 --- a/SignalServiceKit/src/Messages/TSCall+SDS.swift +++ b/SignalServiceKit/src/Messages/TSCall+SDS.swift @@ -125,7 +125,10 @@ class TSCallSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let storyAuthorUuidString: String? = nil + let storyTimestamp: UInt64? = nil + let isGroupStoryReply: Bool? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) } } From b88b3ce396507f871984c8c051cbd3c00ba5156b Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 12:23:18 -0700 Subject: [PATCH 05/20] Process incoming story context --- .../Notifications/AppNotifications.swift | 2 + .../TSIncomingMessage+Builder.swift | 28 +++++++---- .../Messages/Interactions/TSInteraction.swift | 5 +- .../Interactions/TSMessage+Builder.swift | 16 ++++++- .../src/Messages/Interactions/TSMessage.m | 3 ++ .../src/Messages/OWSMessageManager.m | 23 +++++++++- .../src/Messages/Stories/StoryMessage.swift | 14 +++++- .../Network/API/Requests/OWSRequestFactory.m | 4 ++ .../Database/Records/InteractionFinder.swift | 46 +++++++++++++++++-- .../src/TestUtils/Factories.swift | 16 ++++++- 10 files changed, 134 insertions(+), 23 deletions(-) diff --git a/SignalMessaging/Notifications/AppNotifications.swift b/SignalMessaging/Notifications/AppNotifications.swift index a064df556f..03d1e799b3 100644 --- a/SignalMessaging/Notifications/AppNotifications.swift +++ b/SignalMessaging/Notifications/AppNotifications.swift @@ -426,6 +426,8 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { public func canNotify(for incomingMessage: TSIncomingMessage, thread: TSThread, transaction: SDSAnyReadTransaction) -> Bool { + guard !incomingMessage.isGroupStoryReply else { return false } + guard isThreadMuted(thread, transaction: transaction) else { return true } diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift index 454bcaabed..d9f2af1124 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -39,7 +39,9 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { serverDeliveryTimestamp: UInt64 = 0, serverGuid: String? = nil, wasReceivedByUD: Bool = false, - isViewOnceMessage: Bool = false) { + isViewOnceMessage: Bool = false, + storyAuthorAddress: SignalServiceAddress? = nil, + storyTimestamp: UInt64? = nil) { super.init(thread: thread, timestamp: timestamp, @@ -48,12 +50,14 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { attachmentIds: attachmentIds, expiresInSeconds: expiresInSeconds, // expireStartedAt is always initialized to zero for incoming messages. - expireStartedAt: 0, - quotedMessage: quotedMessage, - contactShare: contactShare, - linkPreview: linkPreview, - messageSticker: messageSticker, - isViewOnceMessage: isViewOnceMessage) + expireStartedAt: 0, + quotedMessage: quotedMessage, + contactShare: contactShare, + linkPreview: linkPreview, + messageSticker: messageSticker, + isViewOnceMessage: isViewOnceMessage, + storyAuthorAddress: storyAuthorAddress, + storyTimestamp: storyTimestamp) self.authorAddress = authorAddress self.sourceDeviceId = sourceDeviceId @@ -95,7 +99,9 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { serverDeliveryTimestamp: UInt64, serverGuid: String?, wasReceivedByUD: Bool, - isViewOnceMessage: Bool) -> TSIncomingMessageBuilder { + isViewOnceMessage: Bool, + storyAuthorAddress: SignalServiceAddress?, + storyTimestamp: NSNumber?) -> TSIncomingMessageBuilder { return TSIncomingMessageBuilder(thread: thread, timestamp: timestamp, authorAddress: authorAddress, @@ -112,7 +118,9 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { serverDeliveryTimestamp: serverDeliveryTimestamp, serverGuid: serverGuid, wasReceivedByUD: wasReceivedByUD, - isViewOnceMessage: isViewOnceMessage) + isViewOnceMessage: isViewOnceMessage, + storyAuthorAddress: storyAuthorAddress, + storyTimestamp: storyTimestamp?.uint64Value) } private var hasBuilt = false diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift b/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift index fa57fc8b6d..f46db45829 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -79,7 +79,8 @@ extension TSInteraction { default: return true } - + case let message as TSMessage: + return !message.isGroupStoryReply default: return true } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift b/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift index 8023f77b7a..29df43ee10 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -35,6 +35,14 @@ public class TSMessageBuilder: NSObject { public var messageSticker: MessageSticker? @objc public var isViewOnceMessage = false + @objc + public var storyAuthorAddress: SignalServiceAddress? + @objc + public var storyTimestamp: NSNumber? + @objc + public var isGroupStoryReply: Bool { + storyAuthorAddress != nil && storyTimestamp != nil && thread.isGroupThread + } init(thread: TSThread, timestamp: UInt64? = nil, @@ -47,7 +55,9 @@ public class TSMessageBuilder: NSObject { contactShare: OWSContact? = nil, linkPreview: OWSLinkPreview? = nil, messageSticker: MessageSticker? = nil, - isViewOnceMessage: Bool = false) { + isViewOnceMessage: Bool = false, + storyAuthorAddress: SignalServiceAddress? = nil, + storyTimestamp: UInt64? = nil) { self.thread = thread if let timestamp = timestamp { @@ -65,6 +75,8 @@ public class TSMessageBuilder: NSObject { self.linkPreview = linkPreview self.messageSticker = messageSticker self.isViewOnceMessage = isViewOnceMessage + self.storyAuthorAddress = storyAuthorAddress + self.storyTimestamp = storyTimestamp.map { NSNumber(value: $0) } } @objc diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 1752a0148b..dcb0a7d306 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -92,6 +92,9 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _messageSticker = messageBuilder.messageSticker; _isViewOnceMessage = messageBuilder.isViewOnceMessage; _isViewOnceComplete = NO; + _storyTimestamp = messageBuilder.storyTimestamp; + _storyAuthorUuidString = messageBuilder.storyAuthorAddress.uuidString; + _isGroupStoryReply = messageBuilder.isGroupStoryReply; #ifdef DEBUG [self verifyPerConversationExpiration]; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index d5fe7b2fcf..e9ed8ca60e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -577,7 +577,7 @@ NS_ASSUME_NONNULL_BEGIN } } - if (dataMessage.storyContext != nil) { + if (!SSKFeatureFlags.stories && dataMessage.storyContext != nil) { OWSLogInfo(@"Ignoring message (author: %@, timestamp: %llu) related to story (author: %@, timestamp: %llu)", envelope.sourceAddress, dataMessage.timestamp, @@ -2105,6 +2105,23 @@ NS_ASSUME_NONNULL_BEGIN thread:thread transaction:transaction]; + NSNumber *_Nullable storyTimestamp; + SignalServiceAddress *_Nullable storyAuthorAddress; + if (dataMessage.storyContext != nil && dataMessage.storyContext.hasSentTimestamp + && dataMessage.storyContext.hasAuthorUuid) { + OWSLogInfo( + @"Processing storyContext for message with timestamp: %llu, storyTimestamp: %llu, and author uuid: %@", + envelope.timestamp, + dataMessage.storyContext.sentTimestamp, + dataMessage.storyContext.authorUuid); + SignalServiceAddress *address = + [[SignalServiceAddress alloc] initWithUuidString:dataMessage.storyContext.authorUuid]; + if (address.isValid) { + storyTimestamp = @(dataMessage.storyContext.sentTimestamp); + storyAuthorAddress = address; + } + } + // Legit usage of senderTimestamp when creating an incoming group message record // // The builder() factory method requires us to specify every @@ -2126,7 +2143,9 @@ NS_ASSUME_NONNULL_BEGIN serverDeliveryTimestamp:serverDeliveryTimestamp serverGuid:serverGuid wasReceivedByUD:wasReceivedByUD - isViewOnceMessage:isViewOnceMessage]; + isViewOnceMessage:isViewOnceMessage + storyAuthorAddress:storyAuthorAddress + storyTimestamp:storyTimestamp]; TSIncomingMessage *message = [incomingMessageBuilder build]; if (!message) { OWSFailDebug(@"Missing incomingMessage."); diff --git a/SignalServiceKit/src/Messages/Stories/StoryMessage.swift b/SignalServiceKit/src/Messages/Stories/StoryMessage.swift index 7d8f327ef0..e446030c83 100644 --- a/SignalServiceKit/src/Messages/Stories/StoryMessage.swift +++ b/SignalServiceKit/src/Messages/Stories/StoryMessage.swift @@ -162,7 +162,19 @@ public final class StoryMessage: NSObject, SDSCodableModel { // MARK: - public func anyDidRemove(transaction: SDSAnyWriteTransaction) { - // TODO: Cleanup associated records + // Delete all replies for the message. + InteractionFinder.enumerateReplies(for: self, transaction: transaction) { reply, _ in + reply.anyRemove(transaction: transaction) + } + + // Delete all attachments for the message. + for id in allAttachmentIds { + guard let attachment = TSAttachment.anyFetch(uniqueId: id, transaction: transaction) else { + owsFailDebug("Missing attachment for StoryMessage \(id)") + continue + } + attachment.anyRemove(transaction: transaction) + } } @objc diff --git a/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m b/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m index 73f7bba433..f30a8013af 100644 --- a/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m +++ b/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m @@ -487,6 +487,10 @@ NSString *const OWSRequestKey_AuthKey = @"AuthKey"; capabilities[@"gv1-migration"] = @(YES); capabilities[@"senderKey"] = @(YES); + if (SSKFeatureFlags.stories) { + capabilities[@"stories"] = @(YES); + } + // If the storage service requires (or will require) secondary devices // to have a capability in order to be linked, we might need to always // set that capability here if isSecondaryDevice is true. diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index 697e3f619a..11b2206cde 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -38,6 +38,8 @@ protocol InteractionFinderAdapter { static func interactions(withInteractionIds interactionIds: Set, transaction: ReadTransaction) -> Set + static func enumerateReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) + // MARK: - instance methods func latestInteraction(from address: SignalServiceAddress, transaction: ReadTransaction) -> TSInteraction? @@ -152,7 +154,6 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } unreadInteractionQuery += " WHERE \(sqlClauseForUnreadInteractionCounts(interactionsAlias: "interaction")) " - unreadInteractionQuery += " \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies, interactionsAlias: "interaction")) " guard let unreadInteractionCount = try UInt.fetchOne(transaction.database, sql: unreadInteractionQuery) else { owsFailDebug("unreadInteractionCount was unexpectedly nil") @@ -213,6 +214,13 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } + static func enumerateReplies(for storyMessage: StoryMessage, transaction: SDSAnyReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { + switch transaction.readTransaction { + case .grdbRead(let grdbRead): + GRDBInteractionFinder.enumerateReplies(for: storyMessage, transaction: grdbRead, block: block) + } + } + @objc public class func findMessage( withTimestamp timestamp: UInt64, @@ -388,7 +396,6 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(sqlClauseForAllUnreadInteractions) - \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies)) ORDER BY \(interactionColumn: .id) """ @@ -407,7 +414,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } @objc - public func countUnreadMessages(beforeSortId: UInt64, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: GRDBReadTransaction) -> UInt { + public func countUnreadMessages(beforeSortId: UInt64, transaction: GRDBReadTransaction) -> UInt { do { let sql = """ SELECT COUNT(*) @@ -415,7 +422,6 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) <= ? AND \(sqlClauseForAllUnreadInteractions) - \(GRDBInteractionFinder.filterStoryRepliesClause(for: storyReplyQueryMode)) """ guard let count = try UInt.fetchOne(transaction.database, @@ -585,6 +591,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { return """ ( \(interactionColumn: .read) IS 0 + \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies)) AND \(interactionColumn: .recordType) IN (\(recordTypesSql)) ) """ @@ -600,6 +607,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { return """ \(columnPrefix)\(interactionColumn: .read) IS 0 + \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies, interactionsAlias: interactionsAlias)) AND ( \(columnPrefix)\(interactionColumn: .recordType) IN (\(SDSRecordType.incomingMessage.rawValue), \(SDSRecordType.call.rawValue)) OR ( @@ -892,6 +900,35 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return interactions } + static func enumerateReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { + let sql = """ + SELECT * + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .storyTimestamp) = ? + AND \(interactionColumn: .storyAuthorUuidString) = ? + """ + let cursor = TSInteraction.grdbFetchCursor( + sql: sql, + arguments: [storyMessage.timestamp, storyMessage.authorUuid.uuidString], + transaction: transaction + ) + do { + while let interaction = try cursor.next() { + guard let message = interaction as? TSMessage else { + owsFailDebug("Unexpected object: \(type(of: interaction))") + return + } + var stop: ObjCBool = false + block(message, &stop) + if stop.boolValue { + return + } + } + } catch { + owsFail("error: \(error)") + } + } + static func oldestPlaceholderInteraction(transaction: GRDBReadTransaction) -> OWSRecoverableDecryptionPlaceholder? { let sql = """ SELECT * @@ -1000,6 +1037,7 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(Self.filterStoryRepliesClause(for: .excludeGroupReplies)) AND \(interactionColumn: .errorType) IS NOT ? AND \(interactionColumn: .messageType) IS NOT ? AND \(interactionColumn: .messageType) IS NOT ? diff --git a/SignalServiceKit/src/TestUtils/Factories.swift b/SignalServiceKit/src/TestUtils/Factories.swift index 02d53c648e..81d3536e2c 100644 --- a/SignalServiceKit/src/TestUtils/Factories.swift +++ b/SignalServiceKit/src/TestUtils/Factories.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -288,7 +288,9 @@ public class IncomingMessageFactory: NSObject, Factory { serverDeliveryTimestamp: serverDeliveryTimestampBuilder(), serverGuid: serverGuidBuilder(), wasReceivedByUD: wasReceivedByUDBuilder(), - isViewOnceMessage: isViewOnceMessageBuilder()) + isViewOnceMessage: isViewOnceMessageBuilder(), + storyAuthorAddress: storyAuthorAddressBuilder(), + storyTimestamp: storyTimestampBuilder()) let item = builder.build() item.anyInsert(transaction: transaction) return item @@ -391,6 +393,16 @@ public class IncomingMessageFactory: NSObject, Factory { public var isViewOnceMessageBuilder: () -> Bool = { return false } + + @objc + public var storyAuthorAddressBuilder: () -> SignalServiceAddress? = { + nil + } + + @objc + public var storyTimestampBuilder: () -> NSNumber? = { + nil + } } @objc From 82eb601f39d19fd7a44f57afe814fd4e60a0902f Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 23 Mar 2022 13:53:21 -0700 Subject: [PATCH 06/20] Limit the types of content that can be in a story reply --- .../src/Messages/Interactions/TSMessage.m | 11 +++++++++++ .../src/Messages/OWSMessageManager.m | 16 +++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index dcb0a7d306..d77542514a 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -801,11 +801,22 @@ static const NSUInteger OWSMessageSchemaVersion = 4; - (BOOL)hasRenderableContent { + // Story replies currently only support a subset of message features, so may not + // be renderable in some circumstances where a normal message would be. + if (self.isStoryReply) { + return [self hasRenderableStoryReplyContent]; + } + // We DO NOT consider a message with just a linkPreview // or quotedMessage to be renderable. return (self.body.length > 0 || self.attachmentIds.count > 0 || self.contactShare != nil || self.messageSticker); } +- (BOOL)hasRenderableStoryReplyContent +{ + return self.body.length > 0; +} + #pragma mark - View Once - (void)updateWithViewOnceCompleteAndRemoveRenderableContentWithTransaction:(SDSAnyWriteTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index e9ed8ca60e..800fc92b10 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -2114,11 +2114,13 @@ NS_ASSUME_NONNULL_BEGIN envelope.timestamp, dataMessage.storyContext.sentTimestamp, dataMessage.storyContext.authorUuid); - SignalServiceAddress *address = - [[SignalServiceAddress alloc] initWithUuidString:dataMessage.storyContext.authorUuid]; - if (address.isValid) { - storyTimestamp = @(dataMessage.storyContext.sentTimestamp); - storyAuthorAddress = address; + + storyTimestamp = @(dataMessage.storyContext.sentTimestamp); + storyAuthorAddress = [[SignalServiceAddress alloc] initWithUuidString:dataMessage.storyContext.authorUuid]; + + if (!storyAuthorAddress.isValid) { + OWSFailDebug(@"Discarding story reply with invalid address %@", storyAuthorAddress); + return nil; } } @@ -2158,8 +2160,8 @@ NS_ASSUME_NONNULL_BEGIN // Typically `hasRenderableContent` will depend on whether or not the message has any attachmentIds // But since the message is partially built and doesn't have the attachments yet, we check - // for attachments explicitly. - if (!message.hasRenderableContent && dataMessage.attachments.count == 0) { + // for attachments explicitly. Story replies cannot have attachments, so we can bail on them here immediately. + if (!message.hasRenderableContent && (dataMessage.attachments.count == 0 || message.isStoryReply)) { OWSLogWarn(@"Ignoring empty: %@", messageDescription); OWSLogVerbose(@"Ignoring empty message(envelope): %@", envelope.debugDescription); OWSLogVerbose(@"Ignoring empty message(dataMessage): %@", dataMessage.debugDescription); From d1f95f18a74d1eacad4ebe02528c2badfe396833 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Fri, 25 Mar 2022 20:57:48 -0700 Subject: [PATCH 07/20] Render story reply count and indicator --- Signal.xcodeproj/project.pbxproj | 4 + .../messages-solid-20.imageset/Contents.json | 12 ++ .../messages-solid-20.pdf | Bin 0 -> 4065 bytes .../reply-solid-20.imageset/Contents.json | 12 ++ .../reply-solid-20.pdf | Bin 0 -> 4162 bytes .../ConversationView/CV/CVLoader.swift | 4 +- .../CV/CVMessageMapping.swift | 25 +++- .../StoryContextViewController.swift | 100 ++++++++++++---- .../Context View/StoryItemMediaView.swift | 13 +- .../Context View/StoryReplySheet.swift | 5 + .../Stories/IncomingStoryViewModel.swift | 4 +- .../HomeView/Stories/StoryCell.swift | 19 ++- .../translations/en.lproj/Localizable.strings | 9 ++ SignalServiceKit/src/Contacts/TSThread.h | 7 +- SignalServiceKit/src/Contacts/TSThread.m | 9 -- .../src/Contacts/Threads/TSThread+OWS.swift | 19 ++- .../src/Messages/Interactions/TSMessage.m | 20 ++++ .../src/Messages/Stories/StoryMessage.swift | 4 +- .../Database/Records/InteractionFinder.swift | 113 ++++++++++++++---- .../Storage/Database/SDSDatabaseStorage.swift | 1 + .../src/groups/GroupManager.swift | 2 +- 21 files changed, 308 insertions(+), 74 deletions(-) create mode 100644 Signal/Images.xcassets/messages-solid-20.imageset/Contents.json create mode 100644 Signal/Images.xcassets/messages-solid-20.imageset/messages-solid-20.pdf create mode 100644 Signal/Images.xcassets/reply-solid-20.imageset/Contents.json create mode 100644 Signal/Images.xcassets/reply-solid-20.imageset/reply-solid-20.pdf create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index ee2cfd3fbf..1d42b83a6f 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -861,6 +861,7 @@ 88594E5A2335B08000390B19 /* AudioMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88594E592335B08000390B19 /* AudioMessageView.swift */; }; 885C35502370DFD50004BA35 /* OWSSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885C354F2370DFD50004BA35 /* OWSSyncManager.swift */; }; 8862A55925F090C5005D65DB /* InternalSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8862A55825F090C5005D65DB /* InternalSettingsViewController.swift */; }; + 8864072827EEA658009916B6 /* StoryReplySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072727EEA658009916B6 /* StoryReplySheet.swift */; }; 886A58CA276A760600A1099B /* SubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C8276A760600A1099B /* SubscriptionManager.swift */; }; 886A58CB276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */; }; 886BB3D225BA0C9D00079781 /* PreviewWallpaperViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ABAB8E25B8BE3F0008C78A /* PreviewWallpaperViewController.swift */; }; @@ -2087,6 +2088,7 @@ 88594E592335B08000390B19 /* AudioMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMessageView.swift; sourceTree = ""; }; 885C354F2370DFD50004BA35 /* OWSSyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSSyncManager.swift; sourceTree = ""; }; 8862A55825F090C5005D65DB /* InternalSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSettingsViewController.swift; sourceTree = ""; }; + 8864072727EEA658009916B6 /* StoryReplySheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryReplySheet.swift; sourceTree = ""; }; 886A58C8276A760600A1099B /* SubscriptionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionManager.swift; sourceTree = ""; }; 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionReceiptCredentialRedemptionJob.swift; sourceTree = ""; }; 886CB07724E77E5B00753909 /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = ""; }; @@ -4047,6 +4049,7 @@ 884DB94D27DE67BB00C6A309 /* StoryPageViewController.swift */, 884DB95127DE67D900C6A309 /* StoryItemMediaView.swift */, 884DB95327DEB9E900C6A309 /* StoryPlaybackProgressView.swift */, + 8864072727EEA658009916B6 /* StoryReplySheet.swift */, ); path = "Context View"; sourceTree = ""; @@ -6289,6 +6292,7 @@ 348BB25D20A0C5530047AEC2 /* ContactShareViewHelper.swift in Sources */, 3497971525D6D55400E99FA4 /* PaymentsSendRecipientViewController.swift in Sources */, 884DB94F27DE67BB00C6A309 /* StoryPageViewController.swift in Sources */, + 8864072827EEA658009916B6 /* StoryReplySheet.swift in Sources */, 88A4CC1B246CEC8B0082211F /* DeviceTransferQRScanningViewController.swift in Sources */, 34E95C24269F4F4F004807EC /* CLVViewState.swift in Sources */, 34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */, diff --git a/Signal/Images.xcassets/messages-solid-20.imageset/Contents.json b/Signal/Images.xcassets/messages-solid-20.imageset/Contents.json new file mode 100644 index 0000000000..1952fd6f3f --- /dev/null +++ b/Signal/Images.xcassets/messages-solid-20.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "messages-solid-20.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/messages-solid-20.imageset/messages-solid-20.pdf b/Signal/Images.xcassets/messages-solid-20.imageset/messages-solid-20.pdf new file mode 100644 index 0000000000000000000000000000000000000000..19e22122a1da0c07429ee826f6a5a37dd066a289 GIT binary patch literal 4065 zcmai%c{o&UAIB|I7(yjm%E@C%S;lNq_OVTiC6TeuFc{m2u{X9*G?Fb#Ng_+K6lH73 zPCUkvEM-e$mn_MWyrbvoeR{9n_j>Q^zRo$<@BZE2bD#73{_*)q7;5RrD#*dW5{>j_ z`dseHgRaJAFbseIM4U5NSs9SmA-Iz8o{YOImW0>B+Y#;Yfc$y9yCca7fWwslRaLMT z$rF!t1N$+@7@u>uP~duBYy6qTp{cg4lcVZ2GlgGzj9-r&`~XEABKUk+B;;jkb#^VT zCXJv^>4gW~=X5+Ms@X~Ki{txKlFzCRw-Ub>r%V<{dvHN`u2|2q3tHq`^rowjy;PpH zHM{69NaEg9Q2n$tBj|oZ zrFdBh(`!+>%Xx8ooDkI~C$P~$vI*|l$D5umJYgQI@!%2WSVDErapXGXo{;C9&uSj3 zM5*wFVQ%#WM5BFt$EU0AewrPb{<5ykN*vA@9i2|%9m^J7rPH}P$wZzio0*=|8cge@ z`ccg81|Aybn$*f$@7lI+gska|8YGkt^}H`rJ&8HkQ`Z9%Us(PSjbxe28eo$NiAf;W zQNslzLQ`af3(3=@4wk@;aIKZ%xh9hWlOwiSUG=wQZd^`Dj1)pzOc|+Mu8@2@K3i=Y zuc3kBixfNTWyVY673fND(JE*b?D6mHvJRB8b9Dt*46LfWsB4%SYIjuIK}J)0yszec zM(4PhoLR{*J+={2!V}{yy7o%2uvviq-KH-5FnF6%jtOQOJMbI|#=G19T}#H(t^pK& zF~TK(4@Tt;v5r5ko_Kc>0A(y+1mrK`y@=kPc6cuU{#O7+bSE*^y?|XKUEH(Ih4&%Y;V-WwxLHJT_I;JEuU8A7dd_te1mRXqC6C0IYdUD^~P;$D@r`6HH#hpyE-~P(GGx17^@Ml|t%)N|P0!tCoCPveV9N|$;)e*{} zX1C?+!QsJu?bQeA+ZyP3Zgpy$enyr<7K_WDyPRnPzOEJP#s_bYbXpb`&@JOV0sf8&;`;DLwfAB1YLf$&x4aI zL{}XgS$7V?JQ7M^5Btn}=%L2r+x*!)shV$j51X_5qcjXp@v%0cqHgoXt8a8k<_Vs- z_Baz|sIvM8Nw|vL>e}W5wk`m^cbl0UzCVSf7rSqO=|#w3$r%xL-TL-1Ex$we*aTj^ z8$VmYugYCsFED=iGm}h&O??8pdMi6?w~iV6;Uz82+h*d-`;WQAiDk3oYp31j&OTfj zqZfDOBkSJlnb;IT{$`ln&7&J_C=cNWr@VwYliUSJaGzzYlA2z!LnVs)w>Bse z&j4&Bc;eLz6OT*s8Jrc-)*RGVI$NpkukMd}ba7vv`x~>zDOKs_22?HbS+cxt0xWqY zd7`Z_p&-sBX)ZZX!XU9f**DonE5ttW8@z!yOFCnn2c5V2~N|}X43%Ro0s@cN@Dus4=rDl#<`lX!*t>Wb@6gA2%%Z+_C4bI7%$=ehg zEoQ13WfW#)*Xq_<1iyaO1q|8V)VN!hml>T|i@lG{8n^r7aa=+7i7fN1GtH;$UR;S@ zQk}4nEQv}-$D?D~E*KBI8M$OzMJuCmxlB4q!V3`9Ee}iHV=zy2-ab%jP;R}N<$rhM zB&W(vvzzJs!~8Zf3o^#Cb26eBY0Mrox3bxi&4!`IG#4v7a!aK1gk5Q8&hX^(!hG3L zpV|fTnKY}CVg73UYT6q2n%I`)mI9l~b-(LW?ymzRpX1fx%W;>B%j3V|7M&M!RZpu%sm7=ttnsNS zAj_{~w^_D*HiuXGKTmD;ZyA72fu3+ogR?<8AX7F~PAJELJ>H;(I_A3SFj4s)8*$4w z{4AQ^P(BgL90)F}Lz`ZLax(BIlJou*CVoMFz93ngwic?fS}(S>r&af64F5TPU!6AH zChb1m3)+F&$=c_V&15j8A9X0^6d*^1U-a;yKye?>{I5O3=dMr8e*jN>l7m^^DV-nk zd`{1Arxo3{sl!}3arCh0kx|~AsDt(S^z%ilHkH0~P=7-8^{x1+=95z~OIGXnZ*idH z@Mq!OTFH%8QFiDe^da;Fi-E@TG4E$K%b_3h6K^MGNyGBH^Zz7-m#vreYD!?EOHNwB zRHUf#4e%@os_HeN?~}>Fhw9yN=Z||~9I8`peLw$2k0O3o;u?mt)cM(nqb@$c(AKlxJw5_yl1tA;|zTS>CKHnq1)geB)a2H0rEwX%9sqbr5S2g~nW-itZaMK(7 z+^*9egFig#9zF8L=z&p5jjdDZ;$*XCrKZt1RkqWD+qKE{kn6&*LusM&yJRuz?k~9Z z578eCB;Lo*jbg}`H-a! z&)1k@Ew0$|5Mu3P%bvu8tvW78(7vw`Ic=|e5=`fEqsf_s@`Sew{X zkMfDU3~v2u=u)WR)`WdO{-l96_i(nV)-l*7SZhIVDUPNaq5G(%KGwVM%$t72JjFz0 zeN24w(}^oB#O<*!sKA2-GlsCN%5~7GV@gXOTGmAu?{g^+`bU!=&q!@&ti9+mk2cSI z`~Gdzi)1LF` z_0yM0s#Rqld(w+aibmQ)lZ&mFDn0YxQMbOCBo{aqDOsH%r<17*k=rqEk5=EmV>2H_ zn0dbFMxF5}-M%wXc&la)d1}*YOMYanicG(?lYW@>=HAUuay5ZF*6&qj$Rpd`Z23Ia zY8?Tmww<=^<|iIXfelXFf2>xx-M8NFkWXEmf6#ABE4|?u8(D#p-$~jiHfY#G%cq^x zDbtzNE!3q^HYmdZPgk2hw^NdPCn9R4zrXmJ)LoDfCe{mY5A3q)MZoGO{_gVfPyGD{r@a9A3wV11R+H!lSU?yl(B1ii zhrJn)tpLcMBiMT}IC+=G8EU_S)Gr$t0B-MX$LQ^j zyJ77Ni~&=;rx$_f4k$q66p(=BaZPW6t33c!&_E!RETsV>Z>%RN0AR5C54`syNiz_g z(fE-Vd$btz4L+`Q8VZ3zAuuQs0#$&(70e+JDaK^vT_D=;rU3st<)0(^dEyj2m}lPH~>Fm2sop&*d2iT&ln62V|?6y#vqL2{1!t(|I1#;Z!ri`iSfVxQxAqv zQ2ITF{N=qQPb|R|@A;#1HYNn%0VPIzL?n`c-AXZ9CA7N(5!fxl?z0x7rpib-LJ^L% zL*Nx43NXb}kW&b#qCE_X$3c}4NIV>;3jY5oe|f@-#3;~@zXwx}aV=%Un8B1i%5=OQVg+$gYMcK)c zC9X-fR7kdDizFGY|LETPyFK^*f6wnc=lOom^EsdMIp_JD_j#VzN76`3=PW`-79!a( zyFR;E_~qWqj!uXy00&4oSBR1l0MjA5QwZLSzdM#f&?4YTjsyT^K=5>?xBw_QG@zma zAyd2wSPzIllbMOWr!|7}U5m*_W@GKxNgu;RGj4}!U8X{{PjnX1_1U_!?$73Kh+JwP z=<*j8yN(lDJ&}U+Y2;pCSWv=6Edp5&<5&^Fht!zUsLbIOUCSC=`314dSN0-|JjfYy zGHNG_LEcP(7ZaZNGP(D^Qo5*aTaB_!xcgMVFUdU?XS_Ks_#s?%*v3L44%5>Ha=$;` zp{vt-mor)E6n^Z5j8$E_X;MVxRCxyV;DMzFRv>m$rpi>W<+txMo{GH8RJ{JRR-hVp zoi*`T8=S0L1?~2711aX~-!?7vDSn!EnM>Bsj7&t0?u%<)`ni9TqQN+$D#P?W@VHHGsW1_FKmk6c#DiB{VQb zB=TkY2K4dSA*hzH8v+lbje4hRINUoa>LELnn^F$)QhbH4sM5!g$Yj zgXUtwjzxi+(_5=gf}K?DSA!bkNzG`eouVGh@V?-;~YU2eOUAtKe zkEAUnE(F0lcUVJvh5Z#|?~Uw|#*TL2{e@LK7sbpI$5bE$PscyS!Wiu<4DplFOs{$| zq-BJ4{_*uDcv1i)WBL*RyF?(9e7x}lG9dSdL6hW3Va$_(eWhO7*WjP=_UHdnu8}th zZ$h8|){LN9I)E(z(Zourx@s;Vq9eG08`(80iF!`128Ru zFA-0;gwgoV8$hBF$p8P9{QNSq()>(K#`vRYP4Z1IkB9@38$to>z$-;vKCp3&UZ@ds zR0Pyl>wpgLVQF(m(`QVWXmPwYFUR$8+$j4xk5Ol@ zqe!>2YI8*<)Uo01nHD1`d1NmnNnBo2;vfj3J&aZgh6>Cw-!iFsE{NO%zt~8XMn)l! z{JHG+ABs3WLAhuf_3XR@eao-hOvO)d@&NH##7(HCU z;nCZ~*^k{$+iO)O-*Jf3x7sgE-S9V{ZB%pKgbRdLkkYi7BrQEMT#}Z{M3$E=N9J6< zw~S354>s9V?hc@vtwEP)+3M76%Srd8hp;Ao(58gd9nq@hR5MVu$3eB(sfUKE0BHRH zsCwe=m*Y|HcF7$dIHfq^mX_SfOgU~cr$5TmbHYh4TA~!bW%RDl%@qp-RKtGJ`gyYApX|Ve&%k@Gl8|pk7mXnZ?Q+jxHLp6g;^xZI6@*q zhNnV~p`};_gR|UtU3~08L6HhZwR)`GBxR81FDw$CssYSFi`F~{NOyCiOIcC{IRP2) z`~0>ThfS>Kl!3aq$=oJqf}^+O{WF4jlM(K%%tsPgDfb|9VKQ*$GY*GDLS?;JCIC=G zs4fpn8jFlBGf@rvSY72c2%?U7&E$LC)eAHf7Vr{$F&yo6&<1?@*�u&QOdRB&t;( z&NLB5WDEby!*^f3B#}R#J40iHN7#z(s;0WpIbN3MnlXty$!gzULW_=_x?Ym2X{5aI z3q`1&&G!2CJ=T|i+`(O@poqh_nFq0lUW1;7j#Vm(uwmMI-)s5v-DMSc_IB!gEx!s^ zO`E`!@MqAONc*-FHnnaxmH{0LHsLibjYJCxro$)PlEm|wi?uTox$=eU;&qd*%&;7c z&c)t7#@{J89c@XS!kJ@7aOdH}xAC{l4Fu-UPCT+<-@_l^h94DJNIg{bWk(;r6MweD z+#$iix-$I{%WLeCq6k;lGr=L)a&QgzD77JUvHj2#bj`o<)CBKpICm@g zGw#}nW=M!g$nnVpvCbB$M0P8%TR8WLG(f^#!jwmdZJup6;&7XsaafTdMLdeWz9L(2FU&dQlPrmn5V!s>4k6T*Ymn%Fw10DLubBV!V%8s@WNqOXL9dlHAFv zMz>Bvc@58tXlsmJR5)L!c2(`F=7URziadubN^aL@Ss6BI1)UFqVNzt%zobw1l%4h3a455Z=H=G4lEJ!IXvP8)6@u zHrNT01nb$8vm&!XvsEuH9c+U;Y{^~{d_Ya1Hc%73$j#N!rfF}{6-2weM(Nb=r#w$x zOBGLbN@Yq-C^0Z24f76Xm3WjG4#8cau2|PGSNgC_X*{iv=9f*&vX|SN*@^yCSsxQn zyY2e5{gd*i6roI^W+6c#Gof_3PLyD#XJ$|4Z05XNSG9{3#}lyCtkwJzJ9uiwN}s8d zWL2C(Zdv(q;n@L|{P9xdGJH{$g>&A;s{W(4$uidR>NPesCVm=*`Y;QaeTDIAu8MI^ zSx$Znro}qs#j}^d8;3acJFP{zvAHeSY;4{X{+E)Z(t%TXR(Xn@=kQOj#IC7KTSF^j zvhUGcT%dBLsD`flmG3MqAL@+UQ)pM}zLt0O&iB(C%5fHP zS^VSt_GgyQn4DcaBW5XWdBDP>`cvh0`NKlVpwy#5y~4>X z%YNzpL;ZOioC2Q(hRz=I9=I}9E8?9b<0wP5_xH*jGJBgkZMC@FcVRSs-nYY}WBj%9 z*lsQTG`Bu?5+<(eR$a_+pBT?=;@^okm(f}S(~(~r8nMBW{Z1i z<%#yKmRC+47ZwwpGKEOMmW=55_YKik&&sjtn82_Arl?%3yEo!)qY z@T6z##E|hlzX>3oGNX1MTE{--dZgv`07Pk}YdS#P@Dj2InFE6 z7}EXC$gND><2m--)Wde#;{8wcEzTkKAzI72Ye_UrB<4X^TY}H9;?Sskk^C)mTYPfo zqv#olsc&(6uGDn$4*+b~QGrWI<{-XY=Vxt5~btk#{38 zPxH~lK!d=^h0wt)(SFghs(1FTZBREeGOJ!V>(i3@`R?9P$ngta*XWE~i;EG6{`E3N zrM`ORRaSXr`9yD6dWGFuopu!+t3pxK*yS;8!F2e(&bngjRj-YG8xP3#V^7IWLF_63~V zb=k!)P2ZP-7@o>5Q7zjY-Wqi(ZrWJ7H|jvEy5XDiLPHW`*sMO^8vD+=Kh(o%Zt`2i7y7?hc^_5x z*_OexdYYOVSTey8*k{#CfbC!SyU)vi;qTu#O$J~W2#!Ro2FV|=hBI7{`~3$G`!FCI z0l@T$j${TW@6$LV+D|Mj|F3|WSPIsi1Y8Dz25e4h_z>M40VF~lg;KDQ28?~M-jo192x~j zIuT^?3J4qliIhd4&pDzLQ1Uog1VRP!zg_;&1ewCPpdZC8k(AUn)PejTYZy4~ literal 0 HcmV?d00001 diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVLoader.swift b/Signal/src/ViewControllers/ConversationView/CV/CVLoader.swift index 0c24431563..7fe0d9cc5c 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVLoader.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVLoader.swift @@ -161,7 +161,9 @@ public class CVLoader: NSObject { self.benchSteps.step("messageMapping") let thread = threadViewModel.threadRecord - let threadInteractionCount = thread.numberOfInteractions(with: transaction) + let threadInteractionCount = thread.numberOfInteractions( + with: messageMapping.storyReplyQueryMode, + transaction: transaction) self.benchSteps.step("threadInteractionCount") diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift b/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift index f1f5662f05..93a7f912a3 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift @@ -3,6 +3,7 @@ // import Foundation +import SignalServiceKit public class CVMessageMapping: NSObject { @@ -18,11 +19,15 @@ public class CVMessageMapping: NSObject { public var canLoadNewer = false - private let thread: TSThread + public let storyReplyQueryMode: StoryReplyQueryMode - public required init(thread: TSThread) { - self.thread = thread - self.interactionFinder = InteractionFinder(threadUniqueId: thread.uniqueId) + public convenience init(thread: TSThread, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies) { + self.init(threadUniqueId: thread.uniqueId, storyReplyQueryMode: storyReplyQueryMode) + } + + public required init(threadUniqueId: String, storyReplyQueryMode: StoryReplyQueryMode) { + self.interactionFinder = InteractionFinder(threadUniqueId: threadUniqueId) + self.storyReplyQueryMode = storyReplyQueryMode } // The smaller this number is, the faster the conversation can display. @@ -167,7 +172,10 @@ public class CVMessageMapping: NSObject { let count = max(1, min(count, maxInteractionCount)) // The number of visible interactions currently in the conversation. - let conversationSize = interactionFinder.count(excludingPlaceholders: !DebugFlags.showFailedDecryptionPlaceholders.get(), transaction: transaction) + let conversationSize = interactionFinder.count( + excludingPlaceholders: !DebugFlags.showFailedDecryptionPlaceholders.get(), + storyReplyQueryMode: storyReplyQueryMode, + transaction: transaction) guard conversationSize > 0 else { self.loadedInteractions = [] updateCanLoadMore(fetchIndexSet: IndexSet(), conversationSize: conversationSize) @@ -196,6 +204,7 @@ public class CVMessageMapping: NSObject { guard let distanceFromLatest = try self.interactionFinder.distanceFromLatest( interactionUniqueId: interactionUniqueId, excludingPlaceholders: !DebugFlags.showFailedDecryptionPlaceholders.get(), + storyReplyQueryMode: self.storyReplyQueryMode, transaction: transaction) else { throw OWSAssertionError("viewIndex was unexpectedly nil") } @@ -481,6 +490,7 @@ public class CVMessageMapping: NSObject { var newItems: [TSInteraction] = [] try self.interactionFinder.enumerateInteractions(range: nsRange, excludingPlaceholders: !DebugFlags.showFailedDecryptionPlaceholders.get(), + storyReplyQueryMode: self.storyReplyQueryMode, transaction: transaction) { (interaction: TSInteraction, _) in newItems.append(interaction) } @@ -493,6 +503,7 @@ public class CVMessageMapping: NSObject { let interactionIds = try interactionFinder.interactionIds( inRange: nsRange, excludingPlaceholders: !DebugFlags.showFailedDecryptionPlaceholders.get(), + storyReplyQueryMode: storyReplyQueryMode, transaction: transaction) guard !interactionIds.isEmpty else { return [] @@ -572,7 +583,9 @@ public class CVMessageMapping: NSObject { var oldestUnreadInteraction: TSInteraction? private func updateOldestUnreadInteraction(transaction: SDSAnyReadTransaction) throws { - self.oldestUnreadInteraction = try interactionFinder.oldestUnreadInteraction(transaction: transaction.unwrapGrdbRead) + self.oldestUnreadInteraction = try interactionFinder.oldestUnreadInteraction( + storyReplyQueryMode: storyReplyQueryMode, + transaction: transaction.unwrapGrdbRead) } public var debugInteractions: String { diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift index db26594bcd..62feac90aa 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift @@ -6,6 +6,7 @@ import Foundation import SignalServiceKit import UIKit import SignalUI +import BonMot protocol StoryContextViewControllerDelegate: AnyObject { func storyContextViewControllerWantsTransitionToNextContext( @@ -29,18 +30,7 @@ class StoryContextViewController: OWSViewController { private var items = [StoryItem]() var currentItem: StoryItem? { - didSet { - currentItemMediaView?.removeFromSuperview() - - if let currentItem = currentItem { - let itemView = StoryItemMediaView(item: currentItem) - self.currentItemMediaView = itemView - mediaViewContainer.addSubview(itemView) - itemView.autoPinEdgesToSuperviewEdges() - } - - updateProgressState() - } + didSet { currentItemWasUpdated() } } var currentItemMediaView: StoryItemMediaView? @@ -121,6 +111,7 @@ class StoryContextViewController: OWSViewController { private lazy var closeButton = OWSButton(imageName: "x-24", tintColor: .ows_white) private lazy var mediaViewContainer = UIView() + private lazy var replyButton = OWSFlatButton() override func viewDidLoad() { super.viewDidLoad() @@ -138,10 +129,29 @@ class StoryContextViewController: OWSViewController { view.addSubview(mediaViewContainer) + replyButton.setBackgroundColors(upColor: .clear) + replyButton.autoSetDimension(.height, toSize: 64) + replyButton.setTitleColor(Theme.darkThemePrimaryColor) + view.addSubview(replyButton) + replyButton.autoPinEdge(.leading, to: .leading, of: mediaViewContainer) + replyButton.autoPinEdge(.trailing, to: .trailing, of: mediaViewContainer) + + view.addSubview(playbackProgressView) + playbackProgressView.autoPinEdge(.leading, to: .leading, of: mediaViewContainer, withOffset: OWSTableViewController2.defaultHOuterMargin) + playbackProgressView.autoPinEdge(.trailing, to: .trailing, of: mediaViewContainer, withOffset: -OWSTableViewController2.defaultHOuterMargin) + playbackProgressView.autoSetDimension(.height, toSize: 2) + playbackProgressView.isUserInteractionEnabled = false + if UIDevice.current.hasIPhoneXNotch || UIDevice.current.isIPad { + // iPhone with notch or iPad (views/replies rendered below media, media is in a card) mediaViewContainer.layer.cornerRadius = 18 mediaViewContainer.clipsToBounds = true + replyButton.autoPinEdge(.top, to: .bottom, of: mediaViewContainer) + playbackProgressView.autoPinEdge(.bottom, to: .top, of: replyButton, withOffset: -OWSTableViewController2.defaultHOuterMargin) } else { + // iPhone with home button (views/replies rendered on top of media, media is fullscreen) + replyButton.autoPinEdge(.bottom, to: .bottom, of: mediaViewContainer) + playbackProgressView.autoPinEdge(.bottom, to: .top, of: replyButton) mediaViewContainer.autoPinEdge(toSuperviewSafeArea: .bottom) } @@ -162,13 +172,6 @@ class StoryContextViewController: OWSViewController { closeButton.autoPinEdge(toSuperviewSafeArea: .top) closeButton.autoPinEdge(toSuperviewSafeArea: .leading) - view.addSubview(playbackProgressView) - playbackProgressView.autoPinEdge(.leading, to: .leading, of: mediaViewContainer, withOffset: OWSTableViewController2.defaultHOuterMargin) - playbackProgressView.autoPinEdge(.trailing, to: .trailing, of: mediaViewContainer, withOffset: -OWSTableViewController2.defaultHOuterMargin) - playbackProgressView.autoPinEdge(.bottom, to: .bottom, of: mediaViewContainer, withOffset: -OWSTableViewController2.defaultHOuterMargin) - playbackProgressView.autoSetDimension(.height, toSize: 2) - playbackProgressView.isUserInteractionEnabled = false - loadStoryItems { [weak self] storyItems in // If there are no stories for this context, dismiss. guard !storyItems.isEmpty else { @@ -188,6 +191,8 @@ class StoryContextViewController: OWSViewController { } } + private var groupThreadId: String? + private static let maxItemsToRender = 100 private func loadStoryItems(completion: @escaping ([StoryItem]) -> Void) { var storyItems = [StoryItem]() @@ -199,13 +204,26 @@ class StoryContextViewController: OWSViewController { if storyItems.count >= Self.maxItemsToRender { stop.pointee = true } } + let groupThreadId: String? + if case .groupId(let groupId) = self.context { + groupThreadId = TSGroupThread.threadId( + forGroupId: groupId, + transaction: transaction + ) + } else { + groupThreadId = nil + } + DispatchQueue.main.async { + self.groupThreadId = groupThreadId completion(storyItems) } } } private func buildStoryItem(for message: StoryMessage, transaction: SDSAnyReadTransaction) -> StoryItem? { + let replyCount = InteractionFinder.countReplies(for: message, transaction: transaction) + switch message.attachment { case .file(let attachmentId): guard let attachment = TSAttachment.anyFetch(uniqueId: attachmentId, transaction: transaction) else { @@ -213,18 +231,56 @@ class StoryContextViewController: OWSViewController { return nil } if let attachment = attachment as? TSAttachmentPointer { - return .init(message: message, attachment: .pointer(attachment)) + return .init(message: message, numberOfReplies: replyCount, attachment: .pointer(attachment)) } else if let attachment = attachment as? TSAttachmentStream { - return .init(message: message, attachment: .stream(attachment)) + return .init(message: message, numberOfReplies: replyCount, attachment: .stream(attachment)) } else { owsFailDebug("Unexpected attachment type \(type(of: attachment))") return nil } case .text(let attachment): - return .init(message: message, attachment: .text(attachment)) + return .init(message: message, numberOfReplies: replyCount, attachment: .text(attachment)) } } + private func currentItemWasUpdated() { + currentItemMediaView?.removeFromSuperview() + + if let currentItem = currentItem { + let itemView = StoryItemMediaView(item: currentItem) + self.currentItemMediaView = itemView + mediaViewContainer.addSubview(itemView) + itemView.autoPinEdgesToSuperviewEdges() + + replyButton.isHidden = false + + "test" + + let replyButtonText: String + switch currentItem.numberOfReplies { + case 0: + replyButtonText = NSLocalizedString("STORY_REPLY_BUTTON_WITH_NO_REPLIES", comment: "Button for replying to a story with no existing replies.") + case 1: + replyButtonText = NSLocalizedString("STORY_REPLY_BUTTON_WITH_1_REPLY", comment: "Button for replying to a story with a single existing reply.") + default: + let format = NSLocalizedString("STORY_REPLY_BUTTON_WITH_N_REPLIES_FORMAT", comment: "Button for replying to a story with N existing replies. {embeds the number of replies}") + replyButtonText = String(format: format, "\(currentItem.numberOfReplies)") + } + + let semiboldStyle = StringStyle(.font(.systemFont(ofSize: 17, weight: .semibold))) + let attributedReplyButtonText = replyButtonText.styled( + with: .font(.systemFont(ofSize: 17, weight: .regular)), + .xmlRules([.style("bold", semiboldStyle)]) + ) + replyButton.setAttributedTitle(attributedReplyButtonText) + } else { + replyButton.isHidden = true + } + + updateProgressState() + + } + private var pauseTime: CFTimeInterval? private var lastTransitionTime: CFTimeInterval? private static let transitionDuration: CFTimeInterval = 5 diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift index 7671bb45d8..1c0bc32991 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift @@ -157,7 +157,14 @@ class StoryItemMediaView: UIView { addSubview(authorRow) authorRow.autoPinWidthToSuperview(withMargin: OWSTableViewController2.defaultHOuterMargin) - authorRow.autoPinEdge(toSuperviewEdge: .bottom, withInset: OWSTableViewController2.defaultHOuterMargin + 16) + + if UIDevice.current.hasIPhoneXNotch || UIDevice.current.isIPad { + // iPhone with notch or iPad (views/replies rendered below media, media is in a card) + authorRow.autoPinEdge(toSuperviewEdge: .bottom, withInset: OWSTableViewController2.defaultHOuterMargin + 16) + } else { + // iPhone with home button (views/replies rendered on top of media, media is fullscreen) + authorRow.autoPinEdge(toSuperviewEdge: .bottom, withInset: 80) + } } private func buildAvatarView(transaction: SDSAnyReadTransaction) -> UIView { @@ -406,6 +413,7 @@ class StoryItemMediaView: UIView { class StoryItem: NSObject { let message: StoryMessage + let numberOfReplies: UInt enum Attachment { case pointer(TSAttachmentPointer) case stream(TSAttachmentStream) @@ -413,8 +421,9 @@ class StoryItem: NSObject { } var attachment: Attachment - init(message: StoryMessage, attachment: Attachment) { + init(message: StoryMessage, numberOfReplies: UInt, attachment: Attachment) { self.message = message + self.numberOfReplies = numberOfReplies self.attachment = attachment } } diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift new file mode 100644 index 0000000000..77bef79bc6 --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift @@ -0,0 +1,5 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation diff --git a/Signal/src/ViewControllers/HomeView/Stories/IncomingStoryViewModel.swift b/Signal/src/ViewControllers/HomeView/Stories/IncomingStoryViewModel.swift index 6e0764f310..64bde68282 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/IncomingStoryViewModel.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/IncomingStoryViewModel.swift @@ -16,7 +16,7 @@ struct IncomingStoryViewModel: Dependencies { case missing } let latestMessageAttachment: Attachment - let latestMessageHasReplies: Bool + let hasReplies: Bool let latestMessageName: String let latestMessageTimestamp: UInt64 @@ -40,6 +40,7 @@ struct IncomingStoryViewModel: Dependencies { } self.context = latestMessage.context + self.hasReplies = InteractionFinder.hasReplies(for: latestMessage.context, transaction: transaction) if let groupId = latestMessage.groupId { guard let groupThread = TSGroupThread.fetch(groupId: groupId, transaction: transaction) else { @@ -74,7 +75,6 @@ struct IncomingStoryViewModel: Dependencies { latestMessageAttachment = .text(attachment) } - latestMessageHasReplies = false // TODO: replies latestMessageTimestamp = latestMessage.timestamp } diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoryCell.swift b/Signal/src/ViewControllers/HomeView/Stories/StoryCell.swift index e4f8c73180..4ccfd05e35 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/StoryCell.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/StoryCell.swift @@ -13,14 +13,19 @@ class StoryCell: UITableViewCell { let timestampLabel = UILabel() let avatarView = ConversationAvatarView(sizeClass: .fiftySix, localUserDisplayMode: .asUser, useAutolayout: true) let attachmentThumbnail = UIView() + let replyImageView = UIImageView() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .clear - let vStack = UIStackView(arrangedSubviews: [nameLabel, timestampLabel]) + replyImageView.autoSetDimensions(to: CGSize(square: 20)) + replyImageView.contentMode = .scaleAspectFit + + let vStack = UIStackView(arrangedSubviews: [nameLabel, timestampLabel, replyImageView]) vStack.axis = .vertical + vStack.alignment = .leading let hStack = UIStackView(arrangedSubviews: [avatarView, vStack, .hStretchingSpacer(), attachmentThumbnail]) hStack.axis = .horizontal @@ -42,6 +47,18 @@ class StoryCell: UITableViewCell { func configure(with model: IncomingStoryViewModel) { configureTimestamp(with: model) + switch model.context { + case .authorUuid: + replyImageView.image = #imageLiteral(resourceName: "reply-solid-20").withRenderingMode(.alwaysTemplate) + case .groupId: + replyImageView.image = #imageLiteral(resourceName: "messages-solid-20").withRenderingMode(.alwaysTemplate) + case .none: + owsFailDebug("Unexpected context") + } + + replyImageView.isHidden = !model.hasReplies + replyImageView.tintColor = Theme.secondaryTextAndIconColor + nameLabel.font = .ows_dynamicTypeHeadline nameLabel.textColor = Theme.primaryTextColor nameLabel.text = model.latestMessageName diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 68f86ec5ea..d2a4ad2b0e 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -6097,6 +6097,15 @@ /* Tooltip prompting the user to visit a story link. */ "STORY_LINK_PREVIEW_VISIT_LINK_TOOLTIP" = "Visit link"; +/* Button for replying to a story with a single existing reply. */ +"STORY_REPLY_BUTTON_WITH_1_REPLY" = "1 reply"; + +/* Button for replying to a story with N existing replies. {embeds the number of replies} */ +"STORY_REPLY_BUTTON_WITH_N_REPLIES_FORMAT" = "%@ replies"; + +/* Button for replying to a story with no existing replies. */ +"STORY_REPLY_BUTTON_WITH_NO_REPLIES" = "Reply"; + /* Title for the 'Badges' button in sustainer view. */ "SUBSCRIBER_BADGES" = "Badges"; diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 4539d3c82d..dcbd0b7488 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import @@ -92,11 +92,6 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNa #pragma mark Interactions -/** - * @return The number of interactions in this thread. - */ -- (NSUInteger)numberOfInteractionsWithTransaction:(SDSAnyReadTransaction *)transaction; - /** * Get all messages in the thread we weren't able to decrypt */ diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index 806a873a47..f69050c4df 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -354,15 +354,6 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer } #pragma clang diagnostic pop -- (NSUInteger)numberOfInteractionsWithTransaction:(SDSAnyReadTransaction *)transaction -{ - OWSAssertDebug(transaction); - return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] - countExcludingPlaceholders:NO - storyReplyQueryMode:StoryReplyQueryModeInclude - transaction:transaction]; -} - - (nullable TSInteraction *)lastInteractionForInboxWithTransaction:(SDSAnyReadTransaction *)transaction { OWSAssertDebug(transaction); diff --git a/SignalServiceKit/src/Contacts/Threads/TSThread+OWS.swift b/SignalServiceKit/src/Contacts/Threads/TSThread+OWS.swift index 273c938450..5b5bcbad4b 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSThread+OWS.swift +++ b/SignalServiceKit/src/Contacts/Threads/TSThread+OWS.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -171,6 +171,23 @@ public extension TSThread { } lastVisibleInteractionStore.setData(data, key: thread.uniqueId, transaction: transaction) } + + @available(swift, obsoleted: 1.0) + @objc + func numberOfInteractions(transaction: SDSAnyReadTransaction) -> UInt { + numberOfInteractions(transaction: transaction) + } + + func numberOfInteractions( + with storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, + transaction: SDSAnyReadTransaction + ) -> UInt { + InteractionFinder(threadUniqueId: uniqueId).count( + excludingPlaceholders: false, + storyReplyQueryMode: storyReplyQueryMode, + transaction: transaction + ) + } } // MARK: - Drafts diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index d77542514a..3966848881 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -606,6 +606,8 @@ static const NSUInteger OWSMessageSchemaVersion = 4; [super anyDidInsertWithTransaction:transaction]; [self ensurePerConversationExpirationWithTransaction:transaction]; + + [self touchStoryMessageIfNecessaryWithTransaction:transaction]; } - (void)anyWillUpdateWithTransaction:(SDSAnyWriteTransaction *)transaction @@ -624,6 +626,8 @@ static const NSUInteger OWSMessageSchemaVersion = 4; [super anyDidUpdateWithTransaction:transaction]; [self ensurePerConversationExpirationWithTransaction:transaction]; + + [self touchStoryMessageIfNecessaryWithTransaction:transaction]; } - (void)ensurePerConversationExpirationWithTransaction:(SDSAnyWriteTransaction *)transaction @@ -671,6 +675,22 @@ static const NSUInteger OWSMessageSchemaVersion = 4; [self removeAllReactionsWithTransaction:transaction]; [self removeAllMentionsWithTransaction:transaction]; + + [self touchStoryMessageIfNecessaryWithTransaction:transaction]; +} + +- (void)touchStoryMessageIfNecessaryWithTransaction:(SDSAnyWriteTransaction *)transaction +{ + if (!self.isStoryReply) { + return; + } + + StoryMessage *_Nullable storyMessage = [StoryFinder storyWithTimestamp:self.storyTimestamp.unsignedLongLongValue + author:self.storyAuthorAddress + transaction:transaction]; + if (storyMessage) { + [self.databaseStorage touchStoryMessage:storyMessage transaction:transaction]; + } } - (void)removeAllAttachmentsWithTransaction:(SDSAnyWriteTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/Stories/StoryMessage.swift b/SignalServiceKit/src/Messages/Stories/StoryMessage.swift index e446030c83..78795a5c7a 100644 --- a/SignalServiceKit/src/Messages/Stories/StoryMessage.swift +++ b/SignalServiceKit/src/Messages/Stories/StoryMessage.swift @@ -162,8 +162,8 @@ public final class StoryMessage: NSObject, SDSCodableModel { // MARK: - public func anyDidRemove(transaction: SDSAnyWriteTransaction) { - // Delete all replies for the message. - InteractionFinder.enumerateReplies(for: self, transaction: transaction) { reply, _ in + // Delete all group replies for the message. + InteractionFinder.enumerateGroupReplies(for: self, transaction: transaction) { reply, _ in reply.anyRemove(transaction: transaction) } diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index 11b2206cde..abfd85b175 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -5,11 +5,10 @@ import Foundation import GRDB -@objc -public enum StoryReplyQueryMode: UInt { - case include = 0 - case excludeGroupReplies = 1 - case onlyGroupReplies = 2 +public enum StoryReplyQueryMode { + case includeAllReplies + case excludeGroupReplies + case onlyGroupReplies(storyTimestamp: UInt64) } protocol InteractionFinderAdapter { @@ -38,7 +37,9 @@ protocol InteractionFinderAdapter { static func interactions(withInteractionIds interactionIds: Set, transaction: ReadTransaction) -> Set - static func enumerateReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) + static func enumerateGroupReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) + static func countReplies(for storyMessage: StoryMessage, transaction: ReadTransaction) -> UInt + static func hasReplies(for storyContext: StoryContext, transaction: ReadTransaction) -> Bool // MARK: - instance methods @@ -214,10 +215,24 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - static func enumerateReplies(for storyMessage: StoryMessage, transaction: SDSAnyReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { + public static func enumerateGroupReplies(for storyMessage: StoryMessage, transaction: SDSAnyReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { switch transaction.readTransaction { case .grdbRead(let grdbRead): - GRDBInteractionFinder.enumerateReplies(for: storyMessage, transaction: grdbRead, block: block) + GRDBInteractionFinder.enumerateGroupReplies(for: storyMessage, transaction: grdbRead, block: block) + } + } + + public static func countReplies(for storyMessage: StoryMessage, transaction: SDSAnyReadTransaction) -> UInt { + switch transaction.readTransaction { + case .grdbRead(let grdbRead): + return GRDBInteractionFinder.countReplies(for: storyMessage, transaction: grdbRead) + } + } + + public static func hasReplies(for storyContext: StoryContext, transaction: SDSAnyReadTransaction) -> Bool { + switch transaction.readTransaction { + case .grdbRead(let grdbRead): + return GRDBInteractionFinder.hasReplies(for: storyContext, transaction: grdbRead) } } @@ -312,7 +327,6 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - @objc(countExcludingPlaceholders:storyReplyQueryMode:transaction:) public func count(excludingPlaceholders excludePlaceholders: Bool = true, storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies, transaction: SDSAnyReadTransaction) -> UInt { return Bench(title: "InteractionFinder.countExcludingPlaceholders_\(excludePlaceholders)_StoryReplyQueryMode_\(storyReplyQueryMode)") { switch transaction.readTransaction { @@ -395,7 +409,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? - AND \(sqlClauseForAllUnreadInteractions) + AND \(sqlClauseForAllUnreadInteractions()) ORDER BY \(interactionColumn: .id) """ @@ -421,7 +435,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) <= ? - AND \(sqlClauseForAllUnreadInteractions) + AND \(sqlClauseForAllUnreadInteractions()) """ guard let count = try UInt.fetchOne(transaction.database, @@ -448,7 +462,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) <= ? - AND \(sqlClauseForAllUnreadInteractions) + AND \(sqlClauseForAllUnreadInteractions()) ORDER BY \(interactionColumn: .id) """ @@ -516,12 +530,12 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { return cursor.compactMap { $0 as? TSOutgoingMessage } } - public func oldestUnreadInteraction(transaction: GRDBReadTransaction) throws -> TSInteraction? { + public func oldestUnreadInteraction(storyReplyQueryMode: StoryReplyQueryMode, transaction: GRDBReadTransaction) throws -> TSInteraction? { let sql = """ SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? - AND \(sqlClauseForAllUnreadInteractions) + AND \(sqlClauseForAllUnreadInteractions(for: storyReplyQueryMode)) ORDER BY \(interactionColumn: .id) """ let cursor = TSInteraction.grdbFetchCursor(sql: sql, arguments: [threadUniqueId], transaction: transaction) @@ -571,7 +585,7 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { // MARK: - Unread - private let sqlClauseForAllUnreadInteractions: String = { + private func sqlClauseForAllUnreadInteractions(for storyReplyQueryMode: StoryReplyQueryMode = .excludeGroupReplies) -> String { let recordTypes: [SDSRecordType] = [ .disappearingConfigurationUpdateInfoMessage, .unknownProtocolVersionMessage, @@ -591,11 +605,11 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { return """ ( \(interactionColumn: .read) IS 0 - \(GRDBInteractionFinder.filterStoryRepliesClause(for: .excludeGroupReplies)) + \(GRDBInteractionFinder.filterStoryRepliesClause(for: storyReplyQueryMode)) AND \(interactionColumn: .recordType) IN (\(recordTypesSql)) ) """ - }() + } private static func sqlClauseForUnreadInteractionCounts(interactionsAlias: String? = nil) -> String { let columnPrefix: String @@ -900,12 +914,13 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return interactions } - static func enumerateReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { + static func enumerateGroupReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { let sql = """ SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .storyTimestamp) = ? AND \(interactionColumn: .storyAuthorUuidString) = ? + AND \(interactionColumn: .isGroupStoryReply) = 1 """ let cursor = TSInteraction.grdbFetchCursor( sql: sql, @@ -929,6 +944,62 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } + static func countReplies(for storyMessage: StoryMessage, transaction: GRDBReadTransaction) -> UInt { + do { + let sql: String = """ + SELECT COUNT(*) + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .storyTimestamp) = ? + AND \(interactionColumn: .storyAuthorUuidString) = ? + """ + guard let count = try UInt.fetchOne( + transaction.database, + sql: sql, + arguments: [storyMessage.timestamp, storyMessage.authorUuid.uuidString] + ) else { + throw OWSAssertionError("count was unexpectedly nil") + } + return count + } catch { + owsFail("error: \(error)") + } + } + + static func hasReplies(for storyContext: StoryContext, transaction: GRDBReadTransaction) -> Bool { + let threadUniqueId: String + switch storyContext { + case .groupId(let data): + threadUniqueId = TSGroupThread.threadId( + forGroupId: data, + transaction: transaction.asAnyRead + ) + case .authorUuid(let uuid): + guard let contactThread = TSContactThread.getWithContactAddress( + SignalServiceAddress(uuid: uuid), + transaction: transaction.asAnyRead + ) else { return false } + threadUniqueId = contactThread.uniqueId + case .none: + return false + } + + let sql = """ + SELECT EXISTS( + SELECT 1 + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .threadUniqueId) = ? + AND \(interactionColumn: .storyTimestamp) IS NOT NULL + AND \(interactionColumn: .storyAuthorUuidString) IS NOT NULL + LIMIT 1 + ) + """ + do { + return try Bool.fetchOne(transaction.database, sql: sql, arguments: [threadUniqueId]) ?? false + } catch { + owsFail("error: \(error)") + } + } + static func oldestPlaceholderInteraction(transaction: GRDBReadTransaction) -> OWSRecoverableDecryptionPlaceholder? { let sql = """ SELECT * @@ -1106,9 +1177,9 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { switch queryMode { case .excludeGroupReplies: return "AND \(columnPrefix)\(interactionColumn: .isGroupStoryReply) = 0" - case .onlyGroupReplies: - return "AND \(columnPrefix)\(interactionColumn: .isGroupStoryReply) = 1" - case .include: + case .onlyGroupReplies(let storyTimestamp): + return "AND \(columnPrefix)\(interactionColumn: .isGroupStoryReply) = 1 AND \(columnPrefix)\(interactionColumn: .storyTimestamp) = \(storyTimestamp)" + case .includeAllReplies: return "" } } diff --git a/SignalServiceKit/src/Storage/Database/SDSDatabaseStorage.swift b/SignalServiceKit/src/Storage/Database/SDSDatabaseStorage.swift index e7f5d7c4a4..7c7892b386 100644 --- a/SignalServiceKit/src/Storage/Database/SDSDatabaseStorage.swift +++ b/SignalServiceKit/src/Storage/Database/SDSDatabaseStorage.swift @@ -273,6 +273,7 @@ public class SDSDatabaseStorage: SDSTransactable { } } + @objc(touchStoryMessage:transaction:) public func touch(storyMessage: StoryMessage, transaction: SDSAnyWriteTransaction) { switch transaction.writeTransaction { case .grdbWrite(let grdb): diff --git a/SignalServiceKit/src/groups/GroupManager.swift b/SignalServiceKit/src/groups/GroupManager.swift index 251fee45f3..ca7477880f 100644 --- a/SignalServiceKit/src/groups/GroupManager.swift +++ b/SignalServiceKit/src/groups/GroupManager.swift @@ -1157,7 +1157,7 @@ public class GroupManager: NSObject { sendGroupQuitMessage(inThread: groupThread, transaction: transaction) - let hasMessages = groupThread.numberOfInteractions(with: transaction) > 0 + let hasMessages = groupThread.numberOfInteractions(transaction: transaction) > 0 let infoMessagePolicy: InfoMessagePolicy = hasMessages ? .always : .never var groupMembershipBuilder = oldGroupModel.groupMembership.asBuilder From 6085b8e579eb1eb36c2adbf63ede011f2101d029 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Sun, 27 Mar 2022 16:08:00 -0700 Subject: [PATCH 08/20] Initial rendering of group replies --- Signal.xcodeproj/project.pbxproj | 20 +- .../StoryContextViewController.swift | 84 ++--- .../Context View/StoryGroupReplyCell.swift | 286 +++++++++++++++++ .../Context View/StoryGroupReplyLoader.swift | 303 ++++++++++++++++++ .../Context View/StoryGroupReplySheet.swift | 103 ++++++ .../StoryGroupReplyViewItem.swift | 46 +++ .../Context View/StoryItemMediaView.swift | 14 +- .../Context View/StoryReplySheet.swift | 5 - .../translations/en.lproj/Localizable.strings | 9 + .../contacts/OWSContactsManager.swift | 23 +- .../Database/Records/InteractionFinder.swift | 28 ++ SignalServiceKit/src/Util/Refinery.swift | 10 + SignalUI/Views/Mentions/Mention.swift | 6 +- 13 files changed, 880 insertions(+), 57 deletions(-) create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift delete mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1d42b83a6f..6ffb97a33d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -861,7 +861,10 @@ 88594E5A2335B08000390B19 /* AudioMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88594E592335B08000390B19 /* AudioMessageView.swift */; }; 885C35502370DFD50004BA35 /* OWSSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885C354F2370DFD50004BA35 /* OWSSyncManager.swift */; }; 8862A55925F090C5005D65DB /* InternalSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8862A55825F090C5005D65DB /* InternalSettingsViewController.swift */; }; - 8864072827EEA658009916B6 /* StoryReplySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072727EEA658009916B6 /* StoryReplySheet.swift */; }; + 8864072827EEA658009916B6 /* StoryGroupReplySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072727EEA658009916B6 /* StoryGroupReplySheet.swift */; }; + 8864072A27F0D426009916B6 /* StoryGroupReplyLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */; }; + 8864072C27F0DA38009916B6 /* StoryGroupReplyViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */; }; + 8864072E27F0E8DF009916B6 /* StoryGroupReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */; }; 886A58CA276A760600A1099B /* SubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C8276A760600A1099B /* SubscriptionManager.swift */; }; 886A58CB276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */; }; 886BB3D225BA0C9D00079781 /* PreviewWallpaperViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ABAB8E25B8BE3F0008C78A /* PreviewWallpaperViewController.swift */; }; @@ -2088,7 +2091,10 @@ 88594E592335B08000390B19 /* AudioMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMessageView.swift; sourceTree = ""; }; 885C354F2370DFD50004BA35 /* OWSSyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSSyncManager.swift; sourceTree = ""; }; 8862A55825F090C5005D65DB /* InternalSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSettingsViewController.swift; sourceTree = ""; }; - 8864072727EEA658009916B6 /* StoryReplySheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryReplySheet.swift; sourceTree = ""; }; + 8864072727EEA658009916B6 /* StoryGroupReplySheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplySheet.swift; sourceTree = ""; }; + 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyLoader.swift; sourceTree = ""; }; + 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyViewItem.swift; sourceTree = ""; }; + 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyCell.swift; sourceTree = ""; }; 886A58C8276A760600A1099B /* SubscriptionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionManager.swift; sourceTree = ""; }; 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionReceiptCredentialRedemptionJob.swift; sourceTree = ""; }; 886CB07724E77E5B00753909 /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = ""; }; @@ -4049,7 +4055,10 @@ 884DB94D27DE67BB00C6A309 /* StoryPageViewController.swift */, 884DB95127DE67D900C6A309 /* StoryItemMediaView.swift */, 884DB95327DEB9E900C6A309 /* StoryPlaybackProgressView.swift */, - 8864072727EEA658009916B6 /* StoryReplySheet.swift */, + 8864072727EEA658009916B6 /* StoryGroupReplySheet.swift */, + 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */, + 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */, + 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */, ); path = "Context View"; sourceTree = ""; @@ -6177,6 +6186,7 @@ 34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */, 349767E325B8744700ECE1B0 /* ManageStickersViewController.swift in Sources */, 34A8B3512190A40E00218A25 /* CVMediaAlbumView.swift in Sources */, + 8864072A27F0D426009916B6 /* StoryGroupReplyLoader.swift in Sources */, 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MessageActionsToolbar.swift in Sources */, 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */, @@ -6238,6 +6248,7 @@ 34ACA7EA2733159600E47AD4 /* ChangePhoneNumberCaptchaViewController.swift in Sources */, 8806EF19248DBD7200E764C7 /* NotificationPermissionReminderMegaphone.swift in Sources */, 34404271273AFBBE008C9FA4 /* ChangePhoneNumber2FAViewController.swift in Sources */, + 8864072C27F0DA38009916B6 /* StoryGroupReplyViewItem.swift in Sources */, 343A65951FC47D5E000477A1 /* DebugUISyncMessages.m in Sources */, 88D23D1223CEBFB200B0E74B /* NotificationActionHandler.swift in Sources */, 880C2E02262A19DE006650B6 /* InteractiveSheetViewController.swift in Sources */, @@ -6281,6 +6292,7 @@ 3444E6BB264EDFF300B32E3B /* CVColorOrGradientView.swift in Sources */, 327CF66825ACE7DD00DA0A6F /* GetStartedBannerViewController.swift in Sources */, 34ACA7DF2733159600E47AD4 /* Onboarding2FAViewController.swift in Sources */, + 8864072E27F0E8DF009916B6 /* StoryGroupReplyCell.swift in Sources */, 8862A55925F090C5005D65DB /* InternalSettingsViewController.swift in Sources */, 88A941992409A391000E9700 /* LottieToggleButton.swift in Sources */, 342FFE7A271EF581000AC89F /* UIResponder+OWS.swift in Sources */, @@ -6292,7 +6304,7 @@ 348BB25D20A0C5530047AEC2 /* ContactShareViewHelper.swift in Sources */, 3497971525D6D55400E99FA4 /* PaymentsSendRecipientViewController.swift in Sources */, 884DB94F27DE67BB00C6A309 /* StoryPageViewController.swift in Sources */, - 8864072827EEA658009916B6 /* StoryReplySheet.swift in Sources */, + 8864072827EEA658009916B6 /* StoryGroupReplySheet.swift in Sources */, 88A4CC1B246CEC8B0082211F /* DeviceTransferQRScanningViewController.swift in Sources */, 34E95C24269F4F4F004807EC /* CLVViewState.swift in Sources */, 34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */, diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift index 62feac90aa..64e15a7bcd 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryContextViewController.swift @@ -30,7 +30,9 @@ class StoryContextViewController: OWSViewController { private var items = [StoryItem]() var currentItem: StoryItem? { - didSet { currentItemWasUpdated() } + didSet { + currentItemWasUpdated(messageDidChange: oldValue?.message.uniqueId != currentItem?.message.uniqueId) + } } var currentItemMediaView: StoryItemMediaView? @@ -129,6 +131,21 @@ class StoryContextViewController: OWSViewController { view.addSubview(mediaViewContainer) + replyButton.setPressedBlock { [weak self] in + guard let self = self, let currentItem = self.currentItem else { return } + switch self.context { + case .groupId: + let groupReplyVC = StoryGroupReplySheet(storyMessage: currentItem.message) + groupReplyVC.dismissHandler = { [weak self] in self?.play() } + self.pause() + self.present(groupReplyVC, animated: true) + case .authorUuid: + // todo: 1:1 replies + break + case .none: + owsFailDebug("Unexpected context") + } + } replyButton.setBackgroundColors(upColor: .clear) replyButton.autoSetDimension(.height, toSize: 64) replyButton.setTitleColor(Theme.darkThemePrimaryColor) @@ -191,8 +208,6 @@ class StoryContextViewController: OWSViewController { } } - private var groupThreadId: String? - private static let maxItemsToRender = 100 private func loadStoryItems(completion: @escaping ([StoryItem]) -> Void) { var storyItems = [StoryItem]() @@ -204,18 +219,7 @@ class StoryContextViewController: OWSViewController { if storyItems.count >= Self.maxItemsToRender { stop.pointee = true } } - let groupThreadId: String? - if case .groupId(let groupId) = self.context { - groupThreadId = TSGroupThread.threadId( - forGroupId: groupId, - transaction: transaction - ) - } else { - groupThreadId = nil - } - DispatchQueue.main.async { - self.groupThreadId = groupThreadId completion(storyItems) } } @@ -243,7 +247,7 @@ class StoryContextViewController: OWSViewController { } } - private func currentItemWasUpdated() { + private func currentItemWasUpdated(messageDidChange: Bool) { currentItemMediaView?.removeFromSuperview() if let currentItem = currentItem { @@ -254,8 +258,6 @@ class StoryContextViewController: OWSViewController { replyButton.isHidden = false - "test" - let replyButtonText: String switch currentItem.numberOfReplies { case 0: @@ -277,8 +279,7 @@ class StoryContextViewController: OWSViewController { replyButton.isHidden = true } - updateProgressState() - + if messageDidChange { updateProgressState() } } private var pauseTime: CFTimeInterval? @@ -419,28 +420,39 @@ extension StoryContextViewController: UIGestureRecognizerDelegate { func handleLongPress() { switch pauseGestureRecognizer.state { case .began: - pauseTime = CACurrentMediaTime() - delegate?.storyContextViewControllerDidPause(self) - currentItemMediaView?.pause { - self.playbackProgressView.alpha = 0 - self.closeButton.alpha = 0 - } + pause(hideChrome: true) case .ended: - if let lastTransitionTime = lastTransitionTime, let pauseTime = pauseTime { - let pauseDuration = CACurrentMediaTime() - pauseTime - self.lastTransitionTime = lastTransitionTime + pauseDuration - self.pauseTime = nil - } - currentItemMediaView?.play { - self.playbackProgressView.alpha = 1 - self.closeButton.alpha = 1 - } - delegate?.storyContextViewControllerDidResume(self) + play() default: break } } + func pause(hideChrome: Bool = false) { + guard pauseTime == nil else { return } + pauseTime = CACurrentMediaTime() + delegate?.storyContextViewControllerDidPause(self) + currentItemMediaView?.pause(hideChrome: hideChrome) { + if hideChrome { + self.playbackProgressView.alpha = 0 + self.closeButton.alpha = 0 + } + } + } + + func play() { + if let lastTransitionTime = lastTransitionTime, let pauseTime = pauseTime { + let pauseDuration = CACurrentMediaTime() - pauseTime + self.lastTransitionTime = lastTransitionTime + pauseDuration + self.pauseTime = nil + } + currentItemMediaView?.play { + self.playbackProgressView.alpha = 1 + self.closeButton.alpha = 1 + } + delegate?.storyContextViewControllerDidResume(self) + } + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { let touchLocation = gestureRecognizer.location(in: view) if gestureRecognizer == leftTapGestureRecognizer { @@ -492,7 +504,7 @@ extension StoryContextViewController: DatabaseChangeDelegate { } DispatchQueue.main.async { if shouldDismiss { - self.dismiss(animated: true) + self.presentingViewController?.dismiss(animated: true) } else { self.items = newItems self.currentItem = currentItem diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift new file mode 100644 index 0000000000..d9c99c53d7 --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift @@ -0,0 +1,286 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import UIKit +import SignalMessaging + +class StoryGroupReplyCell: UITableViewCell { + lazy var avatarView = ConversationAvatarView(sizeClass: .twentyEight, localUserDisplayMode: .asUser, useAutolayout: true) + lazy var messageLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + lazy var authorNameLabel: UILabel = { + let label = UILabel() + label.font = UIFont.ows_dynamicTypeFootnoteClamped.ows_semibold + return label + }() + lazy var timestampLabel: UILabel = { + let label = UILabel() + label.font = UIFont.ows_dynamicTypeCaption1Clamped + label.textColor = .ows_gray25 + label.textAlignment = .trailing + return label + }() + lazy var bubbleView: UIView = { + let view = UIView() + view.backgroundColor = .ows_gray80 + view.layoutMargins = UIEdgeInsets(hMargin: 12, vMargin: 7) + return view + }() + lazy var bubbleCornerMaskLayer = CAShapeLayer() + + enum CellType: String, CaseIterable { + case standalone + case top + case bottom + case middle + case reaction + } + let cellType: CellType + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + if let reuseIdentifier = reuseIdentifier, let cellType = CellType(rawValue: reuseIdentifier) { + self.cellType = cellType + } else { + owsFailDebug("Missing cellType for reuseIdentifer \(String(describing: reuseIdentifier))") + self.cellType = .standalone + } + + super.init(style: style, reuseIdentifier: reuseIdentifier) + + selectionStyle = .none + backgroundColor = .clear + + let vStack = UIStackView() + vStack.axis = .vertical + vStack.spacing = 2 + + let hStack = UIStackView() + hStack.axis = .horizontal + hStack.alignment = .bottom + hStack.isLayoutMarginsRelativeArrangement = true + + switch cellType { + case .standalone: + hStack.layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 6) + avatarView.autoSetDimensions(to: CGSize(square: 28)) + hStack.addArrangedSubview(avatarView) + hStack.addArrangedSubview(.spacer(withWidth: 8)) + hStack.addArrangedSubview(bubbleView) + hStack.addArrangedSubview(.hStretchingSpacer()) + + bubbleView.layer.cornerRadius = 18 + bubbleView.clipsToBounds = true + + bubbleView.addSubview(vStack) + vStack.autoPinEdgesToSuperviewMargins() + + vStack.addArrangedSubview(authorNameLabel) + vStack.addArrangedSubview(messageLabel) + case .top: + hStack.layoutMargins = UIEdgeInsets(top: 6, leading: 16, bottom: 1, trailing: 16) + hStack.addArrangedSubview(.spacer(withWidth: 36)) + hStack.addArrangedSubview(bubbleView) + hStack.addArrangedSubview(.hStretchingSpacer()) + + bubbleView.layer.mask = bubbleCornerMaskLayer + + bubbleView.addSubview(vStack) + vStack.autoPinEdgesToSuperviewMargins() + + vStack.addArrangedSubview(authorNameLabel) + vStack.addArrangedSubview(messageLabel) + case .bottom: + hStack.layoutMargins = UIEdgeInsets(top: 1, leading: 16, bottom: 6, trailing: 16) + hStack.addArrangedSubview(avatarView) + hStack.addArrangedSubview(.spacer(withWidth: 8)) + hStack.addArrangedSubview(bubbleView) + hStack.addArrangedSubview(.hStretchingSpacer()) + + bubbleView.layer.mask = bubbleCornerMaskLayer + + bubbleView.addSubview(vStack) + vStack.autoPinEdgesToSuperviewMargins() + + vStack.addArrangedSubview(authorNameLabel) + vStack.addArrangedSubview(messageLabel) + case .middle: + hStack.layoutMargins = UIEdgeInsets(top: 1, leading: 16, bottom: 1, trailing: 16) + hStack.addArrangedSubview(.spacer(withWidth: 36)) + hStack.addArrangedSubview(bubbleView) + hStack.addArrangedSubview(.hStretchingSpacer()) + + bubbleView.layer.mask = bubbleCornerMaskLayer + + bubbleView.addSubview(vStack) + vStack.autoPinEdgesToSuperviewMargins() + + vStack.addArrangedSubview(messageLabel) + case .reaction: + break + } + + contentView.addSubview(hStack) + hStack.autoPinEdgesToSuperviewEdges() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(with item: StoryGroupReplyViewItem) { + switch cellType { + case .standalone: + authorNameLabel.textColor = item.authorColor + authorNameLabel.text = item.authorDisplayName + avatarView.updateWithSneakyTransactionIfNecessary { config in + config.dataSource = .address(item.authorAddress) + } + case .top: + authorNameLabel.textColor = item.authorColor + authorNameLabel.text = item.authorDisplayName + case .bottom: + avatarView.updateWithSneakyTransactionIfNecessary { config in + config.dataSource = .address(item.authorAddress) + } + case .middle: + break + case .reaction: + break + } + + configureTextAndTimestamp(for: item) + } + + func configureTextAndTimestamp(for item: StoryGroupReplyViewItem) { + if let displayableText = item.displayableText { + messageLabel.attributedText = displayableText.displayAttributedText.styled( + with: .font(.ows_dynamicTypeBodyClamped), + .color(.ows_gray05), + .alignment(displayableText.displayTextNaturalAlignment) + ) + } + + switch cellType { + case .standalone, .bottom: + // Append timestamp to attributed text + guard let messageText = messageLabel.attributedText else { break } + + let timestampText = item.timeString.styled( + with: .font(.ows_dynamicTypeCaption1Clamped), + .color(.ows_gray25) + ) + + let maxMessageWidth = min(512, CurrentAppContext().frame.width) - 92 + let timestampSpacer: CGFloat = 6 + + let (messageSize, messageLastLineSize) = size(for: messageText, maxWidth: maxMessageWidth) + let (timestampSize, _) = size(for: timestampText, maxWidth: maxMessageWidth) + + let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageLastLineSize.width + let shouldRenderTimestampOnLastMessageLine = lastLineFreeSpace >= timestampSize.width + + if shouldRenderTimestampOnLastMessageLine { + var possibleMessageBubbleWidths = [ + messageSize.width, + messageLastLineSize.width + timestampSpacer + timestampSize.width + ] + if cellType == .standalone { + contentView.layoutIfNeeded() + possibleMessageBubbleWidths.append(authorNameLabel.width) + } + + let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! + + messageLabel.attributedText = .composed(of: [ + messageText, + "\n", + timestampText.styled( + with: .paragraphSpacingBefore(-timestampSize.height), + .firstLineHeadIndent(finalMessageLabelWidth - timestampSize.width) + ) + ]) + } else { + var possibleMessageBubbleWidths = [ + messageSize.width, + timestampSize.width + ] + if cellType == .standalone { + contentView.layoutIfNeeded() + possibleMessageBubbleWidths.append(authorNameLabel.width) + } + + let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! + + messageLabel.attributedText = .composed(of: [ + messageText, + "\n", + timestampText.styled( + with: .firstLineHeadIndent(finalMessageLabelWidth - timestampSize.width) + ) + ]) + } + break + case .top, .middle: + break // No timestamp + case .reaction: + timestampLabel.text = item.timeString + } + } + + private func size(for attributedString: NSAttributedString, maxWidth: CGFloat) -> (size: CGSize, lastLineSize: CGSize) { + guard !attributedString.isEmpty else { return (.zero, .zero) } + + let layoutManager = NSLayoutManager() + let textContainer = NSTextContainer(size: CGSize(width: maxWidth, height: .infinity)) + let textStorage = NSTextStorage() + + layoutManager.addTextContainer(textContainer) + textStorage.addLayoutManager(layoutManager) + textStorage.setAttributedString(attributedString) + + textContainer.lineFragmentPadding = 0 + textContainer.lineBreakMode = .byWordWrapping + textContainer.maximumNumberOfLines = 0 + + let lastGlyphIndex = layoutManager.glyphIndexForCharacter(at: attributedString.length - 1) + let lastLineFragmentRect = layoutManager.lineFragmentUsedRect( + forGlyphAt: lastGlyphIndex, + effectiveRange: nil + ) + + let fullTextRect = layoutManager.usedRect(for: textContainer) + + return (fullTextRect.size, lastLineFragmentRect.size) + } + + override func layoutSubviews() { + super.layoutSubviews() + + let sharpCorners: UIRectCorner + + switch cellType { + case .standalone, .reaction: + // No special corner rounding to apply + return + case .middle: + sharpCorners = [.bottomLeft, .topLeft] + case .top: + sharpCorners = .bottomLeft + case .bottom: + sharpCorners = .topLeft + } + + bubbleView.layoutIfNeeded() + bubbleCornerMaskLayer.path = UIBezierPath.roundedRect( + bubbleView.bounds, + sharpCorners: sharpCorners, + sharpCornerRadius: 4, + wideCornerRadius: 18 + ).cgPath + } +} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift new file mode 100644 index 0000000000..5ead3037dc --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift @@ -0,0 +1,303 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import SignalCoreKit + +class StoryGroupReplyLoader: Dependencies { + private let loadingLock = UnfairLock() + private var messageMapping: CVMessageMapping! + private var threadUniqueId: String! + private let storyMessage: StoryMessage + private weak var tableView: UITableView? + private var replyUniqueIds = [String]() { + didSet { AssertIsOnMainThread() } + } + private var replyItems = [String: StoryGroupReplyViewItem]() { + didSet { AssertIsOnMainThread() } + } + + var numberOfRows: Int { replyUniqueIds.count } + + var oldestLoadedRow: Int? { + let loadedInteractions = loadingLock.withLock { messageMapping.loadedInteractions } + guard let uniqueId = loadedInteractions.first?.uniqueId else { return nil } + return replyUniqueIds.firstIndex(of: uniqueId) + } + + var newestLoadedRow: Int? { + let loadedInteractions = loadingLock.withLock { messageMapping.loadedInteractions } + guard let uniqueId = loadedInteractions.last?.uniqueId else { return nil } + return replyUniqueIds.firstIndex(of: uniqueId) + } + + var isScrolledToBottom: Bool { + AssertIsOnMainThread() + return tableView?.indexPathsForVisibleRows?.lazy.map { $0.row }.contains(replyUniqueIds.count - 1) ?? false + } + + init?(storyMessage: StoryMessage, tableView: UITableView) { + guard case .groupId(let groupId) = storyMessage.context else { + owsFailDebug("Unexpected story message context") + return nil + } + + self.storyMessage = storyMessage + self.tableView = tableView + + // Load the first page synchronously. + databaseStorage.read { transaction in + threadUniqueId = TSGroupThread.threadId(forGroupId: groupId, transaction: transaction) + messageMapping = CVMessageMapping( + threadUniqueId: threadUniqueId, + storyReplyQueryMode: .onlyGroupReplies(storyTimestamp: storyMessage.timestamp) + ) + + load(mode: .initial, transaction: transaction) + } + + databaseStorage.appendDatabaseChangeDelegate(self) + } + + func replyItem(for indexPath: IndexPath) -> StoryGroupReplyViewItem? { + guard let uniqueId = replyUniqueIds[safe: indexPath.row] else { + owsFailDebug("Unexpectedly missing uniqueId for indexPath \(indexPath)") + return nil + } + + return replyItems[uniqueId] + } + + func scrollToBottomOfLoadWindow(animated: Bool) { + guard numberOfRows > 0 else { return } + tableView?.scrollToRow(at: IndexPath(row: numberOfRows - 1, section: 0), at: .bottom, animated: animated) + } + + func loadNewerPageIfNecessary() { + LoadingMode.newer.async { + guard self.loadingLock.withLock({ self.messageMapping.canLoadNewer }) else { return } + self.databaseStorage.read { self.load(mode: .newer, transaction: $0) } + } + } + + func loadOlderPageIfNecessary() { + LoadingMode.older.async { + guard self.loadingLock.withLock({ self.messageMapping.canLoadOlder }) else { return } + self.databaseStorage.read { self.load(mode: .older, transaction: $0) } + } + } + + func reload( + updatedInteractionIds: Set? = nil, + deletedInteractionIds: Set? = nil, + canReuseInteractions: Bool = true + ) { + LoadingMode.reload.async { + self.databaseStorage.read { self.load( + mode: .reload, + canReuseInteractions: canReuseInteractions, + updatedInteractionIds: updatedInteractionIds, + deletedInteractionIds: deletedInteractionIds, + transaction: $0 + ) } + } + } + + private enum LoadingMode: Equatable { + case initial + case older + case newer + case reload + + func async(block: @escaping () -> Void) { + switch self { + case .initial: + DispatchMainThreadSafe(block) + default: + DispatchQueue.sharedUserInteractive.async(execute: block) + } + } + + var queue: DispatchQueue { + switch self { + case .initial: return .main + default: return .sharedUserInteractive + } + } + } + + private func load( + mode: LoadingMode, + canReuseInteractions: Bool = true, + updatedInteractionIds: Set? = nil, + deletedInteractionIds: Set? = nil, + transaction: SDSAnyReadTransaction + ) { + assertOnQueue(mode.queue) + + Logger.info("Loading \(mode)") + + let reusableInteractions: [String: TSInteraction] + if canReuseInteractions { + let loadedInteractions = loadingLock.withLock { messageMapping.loadedInteractions } + reusableInteractions = loadedInteractions.reduce( + into: [String: TSInteraction]() + ) { partialResult, interaction in + guard updatedInteractionIds?.contains(interaction.uniqueId) != true else { return } + partialResult[interaction.uniqueId] = interaction + } + } else { + reusableInteractions = [:] + } + + loadingLock.withLock { + do { + switch mode { + case .initial: + try self.messageMapping.loadNewestMessagePage( + reusableInteractions: reusableInteractions, + deletedInteractionIds: nil, + transaction: transaction + ) + case .newer: + try self.messageMapping.loadNewerMessagePage( + reusableInteractions: reusableInteractions, + deletedInteractionIds: nil, + transaction: transaction + ) + case .older: + try self.messageMapping.loadOlderMessagePage( + reusableInteractions: reusableInteractions, + deletedInteractionIds: nil, + transaction: transaction + ) + case .reload: + try self.messageMapping.loadSameLocation( + reusableInteractions: reusableInteractions, + deletedInteractionIds: nil, + transaction: transaction + ) + } + } catch { + owsFailDebug("Load failed for mode \(mode): \(error)") + return + } + } + + let newReplyItems = buildItems(reusableInteractionIds: Array(reusableInteractions.keys), transaction: transaction) + let replyUniqueIds = InteractionFinder.groupReplyUniqueIds(for: self.storyMessage, transaction: transaction) + + DispatchQueue.main.async { + let wasScrolledToBottom = self.isScrolledToBottom + let wasInitialLoad = mode == .initial + + self.replyUniqueIds = replyUniqueIds + self.replyItems = newReplyItems + self.tableView?.reloadData() + + if wasScrolledToBottom || wasInitialLoad { self.scrollToBottomOfLoadWindow(animated: !wasInitialLoad) } + } + } + + private func buildItems(reusableInteractionIds: [String], transaction: SDSAnyReadTransaction) -> [String: StoryGroupReplyViewItem] { + guard let groupThread = TSGroupThread.anyFetchGroupThread(uniqueId: threadUniqueId, transaction: transaction) else { + owsFailDebug("Missing group thread for story") + return replyItems + } + + let loadedInteractions = loadingLock.withLock { messageMapping.loadedInteractions } + + var messages = [(SignalServiceAddress, TSMessage)]() + var authorAddresses = Set() + + for interaction in loadedInteractions { + if let outgoingMessage = interaction as? TSOutgoingMessage { + messages.append((tsAccountManager.localAddress!, outgoingMessage)) + authorAddresses.insert(tsAccountManager.localAddress!) + } else if let incomingMessage = interaction as? TSIncomingMessage { + messages.append((incomingMessage.authorAddress, incomingMessage)) + authorAddresses.insert(incomingMessage.authorAddress) + } + } + + let groupNameColors = ChatColors.groupNameColors(forThread: groupThread) + let displayNamesByAddress = contactsManagerImpl.displayNamesByAddress( + for: Array(authorAddresses), + transaction: transaction + ) + + var newReplyItems = [String: StoryGroupReplyViewItem]() + var previousItem: StoryGroupReplyViewItem? + for (authorAddress, message) in messages { + let replyItem: StoryGroupReplyViewItem + if reusableInteractionIds.contains(message.uniqueId), let reusableReplyItem = replyItems[message.uniqueId] { + replyItem = reusableReplyItem + } else { + let displayName = authorAddress.isLocalAddress + ? NSLocalizedString( + "STORY_REPLY_YOU_AUTHOR", + comment: "Author text when the local user author's a story reply" + ) + : displayNamesByAddress[authorAddress] + replyItem = StoryGroupReplyViewItem( + message: message, + authorAddress: authorAddress, + authorDisplayName: displayName, + authorColor: groupNameColors.color(for: authorAddress), + transaction: transaction + ) + } + + newReplyItems[message.uniqueId] = replyItem + + if let previousItem = previousItem { + if previousItem.authorAddress == authorAddress + && previousItem.timeString == replyItem.timeString + && replyItem.cellType != .reaction { + switch previousItem.cellType { + case .standalone: + previousItem.cellType = .top + case .bottom: + previousItem.cellType = .middle + case .top, .middle, .reaction: + break + } + + replyItem.cellType = .bottom + } else { + switch previousItem.cellType { + case .standalone, .reaction, .bottom: + break + case .top: + previousItem.cellType = .standalone + case .middle: + previousItem.cellType = .bottom + } + } + } + + previousItem = replyItem + } + + return newReplyItems + } +} + +extension StoryGroupReplyLoader: DatabaseChangeDelegate { + func databaseChangesDidUpdate(databaseChanges: DatabaseChanges) { + guard let storyId = storyMessage.id, databaseChanges.storyMessageRowIds.contains(storyId) else { return } + reload( + updatedInteractionIds: databaseChanges.interactionUniqueIds, + deletedInteractionIds: databaseChanges.interactionDeletedUniqueIds + ) + } + + func databaseChangesDidUpdateExternally() { + reload(canReuseInteractions: false) + } + + func databaseChangesDidReset() { + reload(canReuseInteractions: false) + } +} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift new file mode 100644 index 0000000000..91d5c512cd --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift @@ -0,0 +1,103 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import UIKit +import SignalServiceKit + +class StoryGroupReplySheet: InteractiveSheetViewController { + private lazy var tableView = UITableView() + + private let storyMessage: StoryMessage + + var dismissHandler: (() -> Void)? + + init(storyMessage: StoryMessage) { + self.storyMessage = storyMessage + } + + public required init() { + fatalError("init() has not been implemented") + } + + fileprivate var replyLoader: StoryGroupReplyLoader? + override func viewDidLoad() { + super.viewDidLoad() + + tableView.delegate = self + tableView.dataSource = self + tableView.separatorStyle = .none + tableView.rowHeight = UITableView.automaticDimension + + contentView.addSubview(tableView) + tableView.autoPinEdgesToSuperviewEdges() + + contentView.backgroundColor = .ows_gray90 + tableView.backgroundColor = .ows_gray90 + + for type in StoryGroupReplyCell.CellType.allCases { + tableView.register(StoryGroupReplyCell.self, forCellReuseIdentifier: type.rawValue) + } + + replyLoader = StoryGroupReplyLoader(storyMessage: storyMessage, tableView: tableView) + } + + override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + super.dismiss(animated: flag) { [dismissHandler] in + completion?() + dismissHandler?() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + replyLoader?.scrollToBottomOfLoadWindow(animated: true) + } +} + +extension StoryGroupReplySheet: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let visibleRows = tableView.indexPathsForVisibleRows?.map({ $0.row }), + !visibleRows.isEmpty, + let oldestLoadedRow = replyLoader?.oldestLoadedRow, + let newestLoadedRow = replyLoader?.newestLoadedRow else { return } + + let rowsFromTop = (visibleRows.min() ?? oldestLoadedRow) - oldestLoadedRow + let rowsFromBottom = newestLoadedRow - (visibleRows.max() ?? newestLoadedRow) + + if rowsFromTop <= 30 { + replyLoader?.loadOlderPageIfNecessary() + } + + if rowsFromBottom <= 30 { + replyLoader?.loadNewerPageIfNecessary() + } + } +} + +extension StoryGroupReplySheet: UITableViewDelegate { + +} + +extension StoryGroupReplySheet: UITableViewDataSource { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let item = replyLoader?.replyItem(for: indexPath) else { + owsFailDebug("Missing item for cell at indexPath \(indexPath)") + return UITableViewCell() + } + + let cell = tableView.dequeueReusableCell(withIdentifier: item.cellType.rawValue, for: indexPath) as! StoryGroupReplyCell + cell.configure(with: item) + + return cell + } + + func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + replyLoader?.numberOfRows ?? 0 + } +} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift new file mode 100644 index 0000000000..c60030f70e --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift @@ -0,0 +1,46 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import SignalServiceKit +import UIKit + +class StoryGroupReplyViewItem: Dependencies { + let displayableText: DisplayableText? + let wasRemotelyDeleted: Bool + let receivedAtTimestamp: UInt64 + let authorDisplayName: String? + let authorAddress: SignalServiceAddress + let authorColor: UIColor + + var cellType: StoryGroupReplyCell.CellType + + var timeString: String { DateUtil.formatMessageTimestampForCVC(receivedAtTimestamp, shouldUseLongFormat: false) } + + init( + message: TSMessage, + authorAddress: SignalServiceAddress, + authorDisplayName: String?, + authorColor: UIColor, + transaction: SDSAnyReadTransaction + ) { + if !message.wasRemotelyDeleted { + self.displayableText = DisplayableText.displayableText( + withMessageBody: .init(text: message.body ?? "", ranges: message.bodyRanges ?? .empty), + mentionStyle: .groupReply, + transaction: transaction + ) + } else { + self.displayableText = nil + } + + self.wasRemotelyDeleted = message.wasRemotelyDeleted + self.receivedAtTimestamp = message.receivedAtTimestamp + self.authorAddress = authorAddress + self.authorDisplayName = authorDisplayName + self.authorColor = authorColor + + self.cellType = .standalone + } +} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift index 1c0bc32991..28ec15fdb4 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryItemMediaView.swift @@ -45,15 +45,17 @@ class StoryItemMediaView: UIView { gradientProtectionView.alpha = 1 } - func pause(animateAlongside: @escaping () -> Void) { + func pause(hideChrome: Bool = false, animateAlongside: @escaping () -> Void) { videoPlayer?.pause() - UIView.animate(withDuration: 0.15, delay: 0, options: [.beginFromCurrentState, .curveEaseInOut]) { - self.authorRow.alpha = 0 - self.gradientProtectionView.alpha = 0 + if hideChrome { + UIView.animate(withDuration: 0.15, delay: 0, options: [.beginFromCurrentState, .curveEaseInOut]) { + self.authorRow.alpha = 0 + self.gradientProtectionView.alpha = 0 + animateAlongside() + } completion: { _ in } + } else { animateAlongside() - } completion: { _ in - } } diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift deleted file mode 100644 index 77bef79bc6..0000000000 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryReplySheet.swift +++ /dev/null @@ -1,5 +0,0 @@ -// -// Copyright (c) 2022 Open Whisper Systems. All rights reserved. -// - -import Foundation diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index d2a4ad2b0e..12c64935a4 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -6106,6 +6106,15 @@ /* Button for replying to a story with no existing replies. */ "STORY_REPLY_BUTTON_WITH_NO_REPLIES" = "Reply"; +/* Text indicating a story has been reacted to */ +"STORY_REPLY_REACTION" = "Reacted to the story"; + +/* placeholder text for replying to a story */ +"STORY_REPLY_TEXT_FIELD_PLACEHOLDER" = "Reply"; + +/* Author text when the local user author's a story reply */ +"STORY_REPLY_YOU_AUTHOR" = "You"; + /* Title for the 'Badges' button in sustainer view. */ "SUBSCRIBER_BADGES" = "Badges"; diff --git a/SignalMessaging/contacts/OWSContactsManager.swift b/SignalMessaging/contacts/OWSContactsManager.swift index 9ef2421e96..e8cfb2b651 100644 --- a/SignalMessaging/contacts/OWSContactsManager.swift +++ b/SignalMessaging/contacts/OWSContactsManager.swift @@ -984,9 +984,11 @@ extension OWSContactsManager { // Rather than being called once for each address, we call it once with all the // addresses and it will use a single database query per step to assign // display names to addresses using different techniques. - @objc(objc_displayNamesForAddresses:transaction:) - func displayNames(for addresses: [SignalServiceAddress], transaction: SDSAnyReadTransaction) -> [String] { - return Refinery(addresses).refine { addresses in + private func displayNamesRefinery( + for addresses: [SignalServiceAddress], + transaction: SDSAnyReadTransaction + ) -> Refinery { + return .init(addresses).refine { addresses in // Prefer a saved name from system contacts, if available. return cachedContactNames(for: addresses, transaction: transaction) }.refine { addresses in @@ -1009,11 +1011,22 @@ extension OWSContactsManager { self.fetchProfile(forUnknownAddress: $0) return self.unknownUserLabel } - }.values.map { - $0! } } + @objc + public func displayNamesByAddress( + for addresses: [SignalServiceAddress], + transaction: SDSAnyReadTransaction + ) -> [SignalServiceAddress: String] { + Dictionary(displayNamesRefinery(for: addresses, transaction: transaction)) + } + + @objc(objc_displayNamesForAddresses:transaction:) + func displayNames(for addresses: [SignalServiceAddress], transaction: SDSAnyReadTransaction) -> [String] { + displayNamesRefinery(for: addresses, transaction: transaction).values.map { $0! } + } + func phoneNumbers(for addresses: [SignalServiceAddress], transaction: SDSAnyReadTransaction) -> [String?] { return Refinery(addresses).refine { diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index abfd85b175..21e44a0cfd 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -40,6 +40,7 @@ protocol InteractionFinderAdapter { static func enumerateGroupReplies(for storyMessage: StoryMessage, transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) static func countReplies(for storyMessage: StoryMessage, transaction: ReadTransaction) -> UInt static func hasReplies(for storyContext: StoryContext, transaction: ReadTransaction) -> Bool + static func groupReplyUniqueIds(for storyMessage: StoryMessage, transaction: ReadTransaction) -> [String] // MARK: - instance methods @@ -236,6 +237,13 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } + public static func groupReplyUniqueIds(for storyMessage: StoryMessage, transaction: SDSAnyReadTransaction) -> [String] { + switch transaction.readTransaction { + case .grdbRead(let grdbRead): + return GRDBInteractionFinder.groupReplyUniqueIds(for: storyMessage, transaction: grdbRead) + } + } + @objc public class func findMessage( withTimestamp timestamp: UInt64, @@ -1000,6 +1008,26 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } + static func groupReplyUniqueIds(for storyMessage: StoryMessage, transaction: GRDBReadTransaction) -> [String] { + do { + let sql: String = """ + SELECT \(interactionColumn: .uniqueId) + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .storyTimestamp) = ? + AND \(interactionColumn: .storyAuthorUuidString) = ? + AND \(interactionColumn: .isGroupStoryReply) = 1 + ORDER BY \(interactionColumn: .id) ASC + """ + return try String.fetchAll( + transaction.database, + sql: sql, + arguments: [storyMessage.timestamp, storyMessage.authorUuid.uuidString] + ) + } catch { + owsFail("error: \(error)") + } + } + static func oldestPlaceholderInteraction(transaction: GRDBReadTransaction) -> OWSRecoverableDecryptionPlaceholder? { let sql = """ SELECT * diff --git a/SignalServiceKit/src/Util/Refinery.swift b/SignalServiceKit/src/Util/Refinery.swift index 4151b48a65..d9dd1b8e56 100644 --- a/SignalServiceKit/src/Util/Refinery.swift +++ b/SignalServiceKit/src/Util/Refinery.swift @@ -115,3 +115,13 @@ public class Refinery { return AnySequence(indexes.lazy.map { keys[$0] }) } } + +public extension Dictionary { + init>(_ refinery: T) { + let keysAndValues: [(Key, Value)] = zip(refinery.keys, refinery.values).compactMap { key, value in + guard let value = value else { return nil } + return (key, value) + } + self.init(uniqueKeysWithValues: keysAndValues) + } +} diff --git a/SignalUI/Views/Mentions/Mention.swift b/SignalUI/Views/Mentions/Mention.swift index 4c55d59c02..db0d2bf7c0 100644 --- a/SignalUI/Views/Mentions/Mention.swift +++ b/SignalUI/Views/Mentions/Mention.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -24,6 +24,7 @@ public class Mention: NSObject { case composingAttachment case quotedReply case longMessageView + case groupReply public static var composing: Self = .incoming } @@ -79,6 +80,9 @@ public class Mention: NSObject { case .longMessageView: attributes[.backgroundColor] = Theme.isDarkThemeEnabled ? UIColor.ows_signalBlueDark : UIColor.ows_blackAlpha20 attributes[.foregroundColor] = Theme.primaryTextColor + case .groupReply: + attributes[.backgroundColor] = UIColor.ows_gray60 + attributes[.foregroundColor] = UIColor.ows_gray05 } return attributes From 49d4a6d89fa45a3b958105b8fd5330c7b5f10095 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 09:15:48 -0700 Subject: [PATCH 09/20] Cleanup for RTL --- .../Context View/StoryGroupReplyCell.swift | 67 ++++++++++++------- .../StoryPlaybackProgressView.swift | 48 ++++++++++--- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift index d9c99c53d7..0193c859cf 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift @@ -157,19 +157,17 @@ class StoryGroupReplyCell: UITableViewCell { } func configureTextAndTimestamp(for item: StoryGroupReplyViewItem) { - if let displayableText = item.displayableText { - messageLabel.attributedText = displayableText.displayAttributedText.styled( - with: .font(.ows_dynamicTypeBodyClamped), - .color(.ows_gray05), - .alignment(displayableText.displayTextNaturalAlignment) - ) - } + guard let displayableText = item.displayableText else { return } + + let messageText = displayableText.displayAttributedText.styled( + with: .font(.ows_dynamicTypeBodyClamped), + .color(.ows_gray05), + .alignment(displayableText.displayTextNaturalAlignment) + ) switch cellType { case .standalone, .bottom: // Append timestamp to attributed text - guard let messageText = messageLabel.attributedText else { break } - let timestampText = item.timeString.styled( with: .font(.ows_dynamicTypeCaption1Clamped), .color(.ows_gray25) @@ -178,16 +176,31 @@ class StoryGroupReplyCell: UITableViewCell { let maxMessageWidth = min(512, CurrentAppContext().frame.width) - 92 let timestampSpacer: CGFloat = 6 - let (messageSize, messageLastLineSize) = size(for: messageText, maxWidth: maxMessageWidth) - let (timestampSize, _) = size(for: timestampText, maxWidth: maxMessageWidth) + let messageMeasurement = measure(messageText, maxWidth: maxMessageWidth) + let timestampMeasurement = measure(timestampText, maxWidth: maxMessageWidth) - let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageLastLineSize.width - let shouldRenderTimestampOnLastMessageLine = lastLineFreeSpace >= timestampSize.width + let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageMeasurement.lastLineRect.width + + let textDirectionMatchesAppDirection: Bool + switch displayableText.displayTextNaturalAlignment { + case .left: + textDirectionMatchesAppDirection = !CurrentAppContext().isRTL + case .right: + textDirectionMatchesAppDirection = CurrentAppContext().isRTL + case .natural: + textDirectionMatchesAppDirection = true + default: + owsFailDebug("Unexpected text alignment") + textDirectionMatchesAppDirection = true + } + + let hasSpacedForTimestampOnLastMessageLine = lastLineFreeSpace >= timestampMeasurement.rect.width + let shouldRenderTimestampOnLastMessageLine = hasSpacedForTimestampOnLastMessageLine && textDirectionMatchesAppDirection if shouldRenderTimestampOnLastMessageLine { var possibleMessageBubbleWidths = [ - messageSize.width, - messageLastLineSize.width + timestampSpacer + timestampSize.width + messageMeasurement.rect.width, + messageMeasurement.lastLineRect.width + timestampSpacer + timestampMeasurement.rect.width ] if cellType == .standalone { contentView.layoutIfNeeded() @@ -200,14 +213,14 @@ class StoryGroupReplyCell: UITableViewCell { messageText, "\n", timestampText.styled( - with: .paragraphSpacingBefore(-timestampSize.height), - .firstLineHeadIndent(finalMessageLabelWidth - timestampSize.width) + with: .paragraphSpacingBefore(-timestampMeasurement.rect.height), + .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) ) ]) } else { var possibleMessageBubbleWidths = [ - messageSize.width, - timestampSize.width + messageMeasurement.rect.width, + timestampMeasurement.rect.width ] if cellType == .standalone { contentView.layoutIfNeeded() @@ -220,19 +233,21 @@ class StoryGroupReplyCell: UITableViewCell { messageText, "\n", timestampText.styled( - with: .firstLineHeadIndent(finalMessageLabelWidth - timestampSize.width) + with: textDirectionMatchesAppDirection + ? .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) + : .alignment(.trailing) ) ]) } break case .top, .middle: - break // No timestamp + messageLabel.attributedText = messageText case .reaction: timestampLabel.text = item.timeString } } - private func size(for attributedString: NSAttributedString, maxWidth: CGFloat) -> (size: CGSize, lastLineSize: CGSize) { + private func measure(_ attributedString: NSAttributedString, maxWidth: CGFloat) -> (rect: CGRect, lastLineRect: CGRect) { guard !attributedString.isEmpty else { return (.zero, .zero) } let layoutManager = NSLayoutManager() @@ -255,7 +270,7 @@ class StoryGroupReplyCell: UITableViewCell { let fullTextRect = layoutManager.usedRect(for: textContainer) - return (fullTextRect.size, lastLineFragmentRect.size) + return (fullTextRect, lastLineFragmentRect) } override func layoutSubviews() { @@ -268,11 +283,11 @@ class StoryGroupReplyCell: UITableViewCell { // No special corner rounding to apply return case .middle: - sharpCorners = [.bottomLeft, .topLeft] + sharpCorners = CurrentAppContext().isRTL ? [.bottomRight, .topRight] : [.bottomLeft, .topLeft] case .top: - sharpCorners = .bottomLeft + sharpCorners = CurrentAppContext().isRTL ? .bottomRight : .bottomLeft case .bottom: - sharpCorners = .topLeft + sharpCorners = CurrentAppContext().isRTL ? .topRight : .topLeft } bubbleView.layoutIfNeeded() diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPlaybackProgressView.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPlaybackProgressView.swift index edb775f2e0..cb2ad7851b 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPlaybackProgressView.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPlaybackProgressView.swift @@ -3,6 +3,7 @@ // import Foundation +import UIKit class StoryPlaybackProgressView: UIView { var playedColor: UIColor = .ows_white { @@ -102,32 +103,59 @@ class StoryPlaybackProgressView: UIView { unplayedShapeLayer.path = unplayedBezierPath.cgPath } - for x in 0.. 0 { + for idx in 0.. 0 { + var playedXPosition = CGFloat(idx) * (itemWidth + itemSpacing) + let playedWidth = itemWidth * itemState.value + let playedRoundedCorners: UIRectCorner + var unplayedXPosition = playedXPosition + playedWidth + let unplayedRoundedCorners: UIRectCorner + + if CurrentAppContext().isRTL { + unplayedXPosition = rect.width - playedXPosition - itemWidth + playedXPosition = unplayedXPosition + itemWidth - playedWidth + playedRoundedCorners = [.topRight, .bottomRight] + unplayedRoundedCorners = [.topLeft, .bottomLeft] + } else { + playedRoundedCorners = [.topLeft, .bottomLeft] + unplayedRoundedCorners = [.topRight, .bottomRight] + } + let playedItemFrame = CGRect( - x: CGFloat(x) * (itemWidth + itemSpacing), + x: playedXPosition, y: 0, - width: itemWidth * itemState.value, + width: playedWidth, height: itemHeight ) - playedBezierPath.append(UIBezierPath(roundedRect: playedItemFrame, byRoundingCorners: [.topLeft, .bottomLeft], cornerRadii: CGSize(square: itemHeight / 2))) + playedBezierPath.append(UIBezierPath( + roundedRect: playedItemFrame, + byRoundingCorners: playedRoundedCorners, + cornerRadii: CGSize(square: itemHeight / 2))) + let unplayedItemFrame = CGRect( - x: playedItemFrame.x + playedItemFrame.width, + x: unplayedXPosition, y: 0, width: itemWidth * (1 - itemState.value), height: itemHeight ) - unplayedBezierPath.append(UIBezierPath(roundedRect: unplayedItemFrame, byRoundingCorners: [.topRight, .bottomRight], cornerRadii: CGSize(square: itemHeight / 2))) + unplayedBezierPath.append(UIBezierPath( + roundedRect: unplayedItemFrame, + byRoundingCorners: unplayedRoundedCorners, + cornerRadii: CGSize(square: itemHeight / 2))) } else { let path: UIBezierPath - if itemState.index < x || (itemState.index == x && itemState.value <= 0) { + if itemState.index < idx || (itemState.index == idx && itemState.value <= 0) { path = unplayedBezierPath } else { - owsAssertDebug(itemState.index > x || (itemState.index == x && itemState.value >= 1)) + owsAssertDebug(itemState.index > idx || (itemState.index == idx && itemState.value >= 1)) path = playedBezierPath } + + var xPosition = CGFloat(idx) * (itemWidth + itemSpacing) + if CurrentAppContext().isRTL { xPosition = rect.width - xPosition - itemWidth } + let itemFrame = CGRect( - x: CGFloat(x) * (itemWidth + itemSpacing), + x: xPosition, y: 0, width: itemWidth, height: itemHeight From 9494498d7086bbd9bd5a68443071c19c2db95564 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 13:29:45 -0700 Subject: [PATCH 10/20] Add support for sending group replies to stories --- Signal.xcodeproj/project.pbxproj | 20 +- .../Contents.json | 12 + .../add-reaction-outline-24.pdf | Bin 0 -> 4400 bytes .../Context View/StoryGroupReplySheet.swift | 103 ------ .../StoryGroupReplyCell.swift | 0 .../StoryGroupReplyLoader.swift | 10 +- .../StoryGroupReplySheet.swift | 277 ++++++++++++++++ .../StoryGroupReplyViewItem.swift | 0 .../StoryReplyInputToolbar.swift | 302 ++++++++++++++++++ SignalMessaging/utils/ThreadUtil.h | 7 +- SignalMessaging/utils/ThreadUtil.m | 11 +- .../src/Devices/OWSRecordTranscriptJob.m | 4 +- .../Messages/Interactions/TSOutgoingMessage.m | 15 + .../Interactions/TSOutgoingMessage.swift | 16 +- .../Database/Records/InteractionFinder.swift | 1 + .../src/TestUtils/Factories.swift | 14 +- .../Views/InputAccessoryViewPlaceholder.swift | 4 +- SignalUI/Views/Mentions/MentionPicker.swift | 18 +- 18 files changed, 691 insertions(+), 123 deletions(-) create mode 100644 Signal/Images.xcassets/add-reaction-outline-24.imageset/Contents.json create mode 100644 Signal/Images.xcassets/add-reaction-outline-24.imageset/add-reaction-outline-24.pdf delete mode 100644 Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift rename Signal/src/ViewControllers/HomeView/Stories/{Context View => Group Reply Sheet}/StoryGroupReplyCell.swift (100%) rename Signal/src/ViewControllers/HomeView/Stories/{Context View => Group Reply Sheet}/StoryGroupReplyLoader.swift (97%) create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift rename Signal/src/ViewControllers/HomeView/Stories/{Context View => Group Reply Sheet}/StoryGroupReplyViewItem.swift (100%) create mode 100644 Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 6ffb97a33d..a91bb9d161 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -865,6 +865,7 @@ 8864072A27F0D426009916B6 /* StoryGroupReplyLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */; }; 8864072C27F0DA38009916B6 /* StoryGroupReplyViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */; }; 8864072E27F0E8DF009916B6 /* StoryGroupReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */; }; + 8864073127F21AD7009916B6 /* StoryReplyInputToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8864073027F21AD7009916B6 /* StoryReplyInputToolbar.swift */; }; 886A58CA276A760600A1099B /* SubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C8276A760600A1099B /* SubscriptionManager.swift */; }; 886A58CB276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */; }; 886BB3D225BA0C9D00079781 /* PreviewWallpaperViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ABAB8E25B8BE3F0008C78A /* PreviewWallpaperViewController.swift */; }; @@ -2095,6 +2096,7 @@ 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyLoader.swift; sourceTree = ""; }; 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyViewItem.swift; sourceTree = ""; }; 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryGroupReplyCell.swift; sourceTree = ""; }; + 8864073027F21AD7009916B6 /* StoryReplyInputToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryReplyInputToolbar.swift; sourceTree = ""; }; 886A58C8276A760600A1099B /* SubscriptionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionManager.swift; sourceTree = ""; }; 886A58C9276A760600A1099B /* SubscriptionReceiptCredentialRedemptionJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionReceiptCredentialRedemptionJob.swift; sourceTree = ""; }; 886CB07724E77E5B00753909 /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = ""; }; @@ -4055,10 +4057,6 @@ 884DB94D27DE67BB00C6A309 /* StoryPageViewController.swift */, 884DB95127DE67D900C6A309 /* StoryItemMediaView.swift */, 884DB95327DEB9E900C6A309 /* StoryPlaybackProgressView.swift */, - 8864072727EEA658009916B6 /* StoryGroupReplySheet.swift */, - 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */, - 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */, - 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */, ); path = "Context View"; sourceTree = ""; @@ -4070,6 +4068,7 @@ 884DB94427DD70F700C6A309 /* IncomingStoryViewModel.swift */, 884DB94627DD754700C6A309 /* StoryCell.swift */, 885275BF27E26775003F2F9B /* TextAttachmentView.swift */, + 8864072F27F21AA7009916B6 /* Group Reply Sheet */, 884DB94A27DE66E000C6A309 /* Context View */, ); path = Stories; @@ -4147,6 +4146,18 @@ path = Individual; sourceTree = ""; }; + 8864072F27F21AA7009916B6 /* Group Reply Sheet */ = { + isa = PBXGroup; + children = ( + 8864072727EEA658009916B6 /* StoryGroupReplySheet.swift */, + 8864072927F0D426009916B6 /* StoryGroupReplyLoader.swift */, + 8864072B27F0DA37009916B6 /* StoryGroupReplyViewItem.swift */, + 8864072D27F0E8DF009916B6 /* StoryGroupReplyCell.swift */, + 8864073027F21AD7009916B6 /* StoryReplyInputToolbar.swift */, + ); + path = "Group Reply Sheet"; + sourceTree = ""; + }; 8871B16D25F007CE00D4E070 /* Profile */ = { isa = PBXGroup; children = ( @@ -6158,6 +6169,7 @@ 8855DF87238F1E0C0066D96F /* EmojiReactorsTableView.swift in Sources */, 3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */, 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */, + 8864073127F21AD7009916B6 /* StoryReplyInputToolbar.swift in Sources */, 326DF2612739F4D90017B789 /* FeaturedBadgeViewController.swift in Sources */, 34ACA7D92733159600E47AD4 /* OnboardingController.swift in Sources */, 88A9729422FB4D02004B4FBF /* LocationPicker.swift in Sources */, diff --git a/Signal/Images.xcassets/add-reaction-outline-24.imageset/Contents.json b/Signal/Images.xcassets/add-reaction-outline-24.imageset/Contents.json new file mode 100644 index 0000000000..5a929fd630 --- /dev/null +++ b/Signal/Images.xcassets/add-reaction-outline-24.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "add-reaction-outline-24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/add-reaction-outline-24.imageset/add-reaction-outline-24.pdf b/Signal/Images.xcassets/add-reaction-outline-24.imageset/add-reaction-outline-24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b41e82b2b7705e90a51518239cb2ce87e268b0af GIT binary patch literal 4400 zcmai&2{=@5`^PO)7z$-iImr@tF1SK`?bi9;))tz4|pfRF~-*%oI9h)X~L85uAZ=ZZ!; zfjvnz6E&T=#A!nI-N2>fQJlJ*%B9#K#0OF~D)x6&kgemuht_UBU&XY!nf$MCaQvwB zb&-8)RFhg+wA-ThSyuC{(3FM7YXZYie(pw5&bc-OdCVYsb!CvNzOIy~pgi_5l>Gs| z0FOI{t~QNH4)QPVZ8>8pFxPVNXLzc+?VjN$H4W>t%0#$mH@rUaZoV_HDi8Y3O6I8m z79cr#&yEkGs(DDVV3UlHnlJmtckyUY@!pv%(Zxrvcb9t;o>#ou%^`cCrMH&A5n#^H zL<1`ILrM9LKCd3J6=2azhlItzUtDI!Xq8wYFzyq$Fw7TS@4+Ta_|66D0UgU|wOW!| zIO#|bg$7UZS9sRKGsknuSVM`OVw*8zkKsk@lkf6{Oy`pFDVpEKi;#IF4o8*scB{U3r z`jF@1%AB+8SdP6&oCu?Iw&ll$BEx$C_uA7X_+kQ*wh)KyoHGjwAPsuG`7 z*pAPB!XmuX-v0>1IOs@uD^=9A=#TM#NbBmaa|u<@UjIc&Y9?r7M!kP)jmye06i#uA zg7Mp660hb4`l>#^S^g#hQq>Z@nW@K8<<`JwfWRfX=Iv_mBfq5TUzU#GTGUej_lxcX zKSOLAwXR}c^>=qD>^(u*UkV#b?_%c$c;56pq1L9^jvUaPwtT?4jFZ$XsnBys}&jx zK>su-x;W#A<5=KSm%68l`zzk*_}@C!c6G7RL*oDwVo)U&zzh&lba8ZX)w_?hLIbBl zR>TSeu)hNQ7DMc}7%Sva4db*@W}to) zgc_sTrqk|h(NR*Kn}~9t+9-wXOr{@qA*RL`YR1NGH+;wHNwklGK&?0<(g0|-5ySdQ zi|K`nz3uyCEOeyjjY!{3+t`3q50uD;j~b=Z(hAi&EBDp~1FRdq2sCT+U`J2<<2c0? zxz3V+l}E&+{CJq>$P)D`J6J_dK<{^x_(Vd5MVPXwa!NR7+RGjH3g&M#^rWWn%x1G8 z`dQyXGou=`C)2s7bE81nrm5WDY;zw{pd0n!=_GUe*N27{OEHiui4dOuOZHxk=4ryqN2 z;CHKNo9@>X+fbVCQB*;u|8VT??n^ zc9AAgyhhIDEbC3?yJA8o4DN1vYgZaqj*SuV{JZY&Kn!KjCLLpr{L5ITJlZscQMyaU zlwOMR+7b-p9g5+xbn$XO2Y3ou_#eH@R@9c>y@q3}r!;$X_?%(@fSx@j^$n&^B^yGX z`$Y04V7%fsJEdxC?>8k+#%C1F@4igmsb!KmU(?DweQA?KAjG0Ifl{uUl6+9bi1N~w zl0vKz7b*Q!hd9nWvLfa5*zY~-+9?X)T4a-KRvM_bB&V(VNrcs8-QPdsCNUBw; zp$7B3m<^q1;Lo5JsF7FsM!Y4m?o?v*PormBQN_WORd zET$sPurg2$?O0Spz)IV>X`U_5xBQa~8$q-!*iF>Kt4&~kcK<6=F&v%Eys=cqAcr8@ ztH}VDBbPoM8|4D!aWH)=R41@d3&$Bs=hbeC;(PVvll)saJI~z&26!kJvo053T2-Jv z+@8~nN1CVcYC_K~YgJbkgq*on8CMoy!A%=4tDShAhe7KOyRyRgUCBFja$a&?iZ67} z6*>^+zP{NmFcQh=@7VbdQKARF)AGuj(YSR)}h1wHa;-E=7Ls{+)~9y?iW|t z6oy`SeXOn0th)6X{TXsuRXmR{l(5vX%dyzFONHh}o6KFGW1nN2t9-9}wiRM|Agasy zA}S%OAu47EI$wvM!G94e$C#hI@67)&i8IM2i8LwZrG|ma2*XImOQ)Aw!w`EO zd!+riJz+$!IGRW!9+~(I3+T!0QP^@teYkh+q5Y4xW$EPvwsf{8HdZzRwiIY5j5Xak zy(fJxeF554WoJxX4KkiHUZ^&QB&DtQ>DzEuMoMOvmaP@s9F)nMD3&g@Dy%fJ&AnUM zf6**n&_rCm#}F&p zGI!eQ+RM1&LH=Ch+}oWJR&O3eY{|@+@Kl6nz~kZ3J(_x-h9~tc>+w~18i#p19%wPF zp)052D*{oXGWuMyO{)80uGiC_H>jl}jUqFcCYUS))&%r!t_W}-_z-7|oT`>94%_ii|gQn=~{NzI6-T{ zC=1W~*~123l4guo4*N96q8HrTo!TcpNsk}b5^m6{)5fVrb|n@hEU@9J%^#L#0QxLwQ71Q`tv3MOi(?NB~hes}f}#1>{RJaa>~b;c|B^ z+W$DA9yYW39XzuxC~BHqx%%0)dakG!Ulwc8f_T7x*a-8fCi{bslq| zv4n;&Li|GKjfLnfvjg-|94ICDb?~54O1oLO6}$}22%jO-lCS>e_S#}QaJDEhHZhk^ zv}myC117lYplV2g8yQh?!%S3~w^67KnhQ~^e~%eiH@KK1HyEdJ9gDDONPY54ZiZIk=2(4!XEy2JU>kkK;J zFLif4%v$QFe>APcH~~&-}rfw~^b!W8#J4iDIqM@tv<`9(1`JfBT{6bFp|yTQs-s03>l$a_f870mnur zjnueTgzw8G-s7yjHv`5I#@VA^N5kLbiD7&+e5RHHhB7YqU%n~(^yJ}g)LvS8<$GIo zd|W@{v!{|-9)8;jogrJ1;ml#T?%`zWt7boDlvR{X_6DYun{U;*7JX?X92uk(+m=b1 z-S*A!ZCndIjvl?zkeO_;>W5jX-f(JMx?g#mJX89l>5T8fp&3DFah`n z+s1^}Dhiz>{VdmNJA*I6-%zPiSynAo#Yg>&n(%(L+p*ajl`=FF(#-ek&3@8gah9J4 zq0tuA)4u62xRNG&XR@$K@^Ey|X~BANcE|&>kzWuoS==PM7kHdau-fg4TUymR&`QdR zmi2f@b+qlt<76{GwI94g_$Mo$qUtHz5_uM`sHlL%qOE~bR@DW}{>I-^Uj7?@|BcgF zKu8m9jX^57cmgI6qKgQW*#F>RHzH&U148N;Yb=qIPidSO?Kc({|3^SYBo685V*4kk zWB(%be*?J0Zxe_BZtZ48ES0Z2A+5CZ07JAZ7USX!2tx#g#Q@Xm3T_xjYd}O;9tM*% zH_@VF8|)5rz_e93;`s;kbgBm3bsajr;=Bii+k9}#*OZ!{oDERS4V zaKLG%h@}$T*~SGpO~UE55;3PzXkjrZ8U_^?hlyE<+epA{Bt%eXG)&YQEiMX0TZzhm c|9_W1-w7;^n4mwN9uy)b2Il5g)=~lg4>aS%WdHyG literal 0 HcmV?d00001 diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift deleted file mode 100644 index 91d5c512cd..0000000000 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplySheet.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2022 Open Whisper Systems. All rights reserved. -// - -import Foundation -import UIKit -import SignalServiceKit - -class StoryGroupReplySheet: InteractiveSheetViewController { - private lazy var tableView = UITableView() - - private let storyMessage: StoryMessage - - var dismissHandler: (() -> Void)? - - init(storyMessage: StoryMessage) { - self.storyMessage = storyMessage - } - - public required init() { - fatalError("init() has not been implemented") - } - - fileprivate var replyLoader: StoryGroupReplyLoader? - override func viewDidLoad() { - super.viewDidLoad() - - tableView.delegate = self - tableView.dataSource = self - tableView.separatorStyle = .none - tableView.rowHeight = UITableView.automaticDimension - - contentView.addSubview(tableView) - tableView.autoPinEdgesToSuperviewEdges() - - contentView.backgroundColor = .ows_gray90 - tableView.backgroundColor = .ows_gray90 - - for type in StoryGroupReplyCell.CellType.allCases { - tableView.register(StoryGroupReplyCell.self, forCellReuseIdentifier: type.rawValue) - } - - replyLoader = StoryGroupReplyLoader(storyMessage: storyMessage, tableView: tableView) - } - - override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { - super.dismiss(animated: flag) { [dismissHandler] in - completion?() - dismissHandler?() - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - replyLoader?.scrollToBottomOfLoadWindow(animated: true) - } -} - -extension StoryGroupReplySheet: UIScrollViewDelegate { - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let visibleRows = tableView.indexPathsForVisibleRows?.map({ $0.row }), - !visibleRows.isEmpty, - let oldestLoadedRow = replyLoader?.oldestLoadedRow, - let newestLoadedRow = replyLoader?.newestLoadedRow else { return } - - let rowsFromTop = (visibleRows.min() ?? oldestLoadedRow) - oldestLoadedRow - let rowsFromBottom = newestLoadedRow - (visibleRows.max() ?? newestLoadedRow) - - if rowsFromTop <= 30 { - replyLoader?.loadOlderPageIfNecessary() - } - - if rowsFromBottom <= 30 { - replyLoader?.loadNewerPageIfNecessary() - } - } -} - -extension StoryGroupReplySheet: UITableViewDelegate { - -} - -extension StoryGroupReplySheet: UITableViewDataSource { - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let item = replyLoader?.replyItem(for: indexPath) else { - owsFailDebug("Missing item for cell at indexPath \(indexPath)") - return UITableViewCell() - } - - let cell = tableView.dequeueReusableCell(withIdentifier: item.cellType.rawValue, for: indexPath) as! StoryGroupReplyCell - cell.configure(with: item) - - return cell - } - - func numberOfSections(in tableView: UITableView) -> Int { - 1 - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - replyLoader?.numberOfRows ?? 0 - } -} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift similarity index 100% rename from Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyCell.swift rename to Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift similarity index 97% rename from Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift rename to Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift index 5ead3037dc..55ac3f1a91 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyLoader.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift @@ -8,7 +8,7 @@ import SignalCoreKit class StoryGroupReplyLoader: Dependencies { private let loadingLock = UnfairLock() private var messageMapping: CVMessageMapping! - private var threadUniqueId: String! + private let threadUniqueId: String private let storyMessage: StoryMessage private weak var tableView: UITableView? private var replyUniqueIds = [String]() { @@ -37,18 +37,18 @@ class StoryGroupReplyLoader: Dependencies { return tableView?.indexPathsForVisibleRows?.lazy.map { $0.row }.contains(replyUniqueIds.count - 1) ?? false } - init?(storyMessage: StoryMessage, tableView: UITableView) { - guard case .groupId(let groupId) = storyMessage.context else { - owsFailDebug("Unexpected story message context") + init?(storyMessage: StoryMessage, threadUniqueId: String?, tableView: UITableView) { + guard let threadUniqueId = threadUniqueId else { + owsFailDebug("Unexpectedly missing threadUniqueId") return nil } + self.threadUniqueId = threadUniqueId self.storyMessage = storyMessage self.tableView = tableView // Load the first page synchronously. databaseStorage.read { transaction in - threadUniqueId = TSGroupThread.threadId(forGroupId: groupId, transaction: transaction) messageMapping = CVMessageMapping( threadUniqueId: threadUniqueId, storyReplyQueryMode: .onlyGroupReplies(storyTimestamp: storyMessage.timestamp) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift new file mode 100644 index 0000000000..c615fcb057 --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift @@ -0,0 +1,277 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import UIKit +import SignalServiceKit + +class StoryGroupReplySheet: InteractiveSheetViewController { + override var renderExternalHandle: Bool { false } + + private lazy var tableView = UITableView() + private lazy var inputToolbar = StoryReplyInputToolbar() + private lazy var inputToolbarBottomConstraint = inputToolbar.autoPinEdge(toSuperviewEdge: .bottom) + + private lazy var inputAccessoryPlaceholder: InputAccessoryViewPlaceholder = { + let placeholder = InputAccessoryViewPlaceholder() + placeholder.delegate = self + placeholder.referenceView = view + return placeholder + }() + + private let storyMessage: StoryMessage + private let thread: TSThread? + + var dismissHandler: (() -> Void)? + + init(storyMessage: StoryMessage) { + self.storyMessage = storyMessage + self.thread = Self.databaseStorage.read { transaction in + if let groupId = storyMessage.groupId { + return TSGroupThread.fetch(groupId: groupId, transaction: transaction) + } else { + owsFailDebug("Unexpectedly received non-group thread for story reply sheet.") + return TSContactThread.getWithContactAddress(storyMessage.authorAddress, transaction: transaction) + } + } + } + + public required init() { + fatalError("init() has not been implemented") + } + + fileprivate var replyLoader: StoryGroupReplyLoader? + override func viewDidLoad() { + super.viewDidLoad() + + tableView.delegate = self + tableView.dataSource = self + tableView.separatorStyle = .none + tableView.rowHeight = UITableView.automaticDimension + tableView.keyboardDismissMode = .interactive + tableView.contentInset = UIEdgeInsets(top: 30, left: 0, bottom: 0, right: 0) + + contentView.addSubview(tableView) + tableView.autoPinEdgesToSuperviewEdges() + + // We add the handle directly to the content view, + // so that it doesn't scroll with the table. + let handleContainer = UIView() + contentView.addSubview(handleContainer) + handleContainer.autoPinWidthToSuperview() + handleContainer.autoPinEdge(toSuperviewEdge: .top) + + let handle = UIView() + handle.backgroundColor = .ows_gray65 + handle.autoSetDimensions(to: CGSize(width: 36, height: 5)) + handle.layer.cornerRadius = 5 / 2 + handleContainer.addSubview(handle) + handle.autoPinHeightToSuperview(withMargin: 12) + handle.autoHCenterInSuperview() + + inputToolbar.delegate = self + contentView.addSubview(inputToolbar) + inputToolbar.autoPinWidthToSuperview() + inputToolbarBottomConstraint.isActive = true + + contentView.backgroundColor = .ows_gray90 + tableView.backgroundColor = .ows_gray90 + handleContainer.backgroundColor = .ows_gray90 + + for type in StoryGroupReplyCell.CellType.allCases { + tableView.register(StoryGroupReplyCell.self, forCellReuseIdentifier: type.rawValue) + } + + replyLoader = StoryGroupReplyLoader(storyMessage: storyMessage, threadUniqueId: thread?.uniqueId, tableView: tableView) + } + + public override var canBecomeFirstResponder: Bool { true } + + public override var inputAccessoryView: UIView? { inputAccessoryPlaceholder } + + override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + super.dismiss(animated: flag) { [dismissHandler] in + completion?() + dismissHandler?() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + replyLoader?.scrollToBottomOfLoadWindow(animated: true) + } +} + +extension StoryGroupReplySheet: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let visibleRows = tableView.indexPathsForVisibleRows?.map({ $0.row }), + !visibleRows.isEmpty, + let oldestLoadedRow = replyLoader?.oldestLoadedRow, + let newestLoadedRow = replyLoader?.newestLoadedRow else { return } + + let rowsFromTop = (visibleRows.min() ?? oldestLoadedRow) - oldestLoadedRow + let rowsFromBottom = newestLoadedRow - (visibleRows.max() ?? newestLoadedRow) + + if rowsFromTop <= 30 { + replyLoader?.loadOlderPageIfNecessary() + } + + if rowsFromBottom <= 30 { + replyLoader?.loadNewerPageIfNecessary() + } + } +} + +extension StoryGroupReplySheet: UITableViewDelegate { + +} + +extension StoryGroupReplySheet: UITableViewDataSource { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let item = replyLoader?.replyItem(for: indexPath) else { + owsFailDebug("Missing item for cell at indexPath \(indexPath)") + return UITableViewCell() + } + + let cell = tableView.dequeueReusableCell(withIdentifier: item.cellType.rawValue, for: indexPath) as! StoryGroupReplyCell + cell.configure(with: item) + + return cell + } + + func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + replyLoader?.numberOfRows ?? 0 + } +} + +extension StoryGroupReplySheet: InputAccessoryViewPlaceholderDelegate { + public func inputAccessoryPlaceholderKeyboardIsPresenting(animationDuration: TimeInterval, animationCurve: UIView.AnimationCurve) { + handleKeyboardStateChange(animationDuration: animationDuration, animationCurve: animationCurve) + } + + public func inputAccessoryPlaceholderKeyboardDidPresent() { + updateInputToolbarPosition() + updateContentInsets(animated: false) + } + + public func inputAccessoryPlaceholderKeyboardIsDismissing(animationDuration: TimeInterval, animationCurve: UIView.AnimationCurve) { + handleKeyboardStateChange(animationDuration: animationDuration, animationCurve: animationCurve) + } + + public func inputAccessoryPlaceholderKeyboardDidDismiss() { + updateInputToolbarPosition() + updateContentInsets(animated: false) + } + + public func inputAccessoryPlaceholderKeyboardIsDismissingInteractively() { + updateInputToolbarPosition() + } + + func handleKeyboardStateChange(animationDuration: TimeInterval, animationCurve: UIView.AnimationCurve) { + guard animationDuration > 0 else { + updateInputToolbarPosition() + updateContentInsets(animated: false) + return + } + + UIView.beginAnimations("keyboardStateChange", context: nil) + UIView.setAnimationBeginsFromCurrentState(true) + UIView.setAnimationCurve(animationCurve) + UIView.setAnimationDuration(animationDuration) + updateInputToolbarPosition() + updateContentInsets(animated: true) + UIView.commitAnimations() + } + + func updateInputToolbarPosition() { + inputToolbarBottomConstraint.constant = -inputAccessoryPlaceholder.keyboardOverlap + + // We always want to apply the new bottom bar position immediately, + // as this only happens during animations (interactive or otherwise) + inputToolbar.superview?.layoutIfNeeded() + } + + func updateContentInsets(animated: Bool) { + let wasScrolledToBottom = replyLoader?.isScrolledToBottom ?? false + tableView.contentInset.bottom = inputAccessoryPlaceholder.keyboardOverlap + inputToolbar.height - view.safeAreaInsets.bottom + if wasScrolledToBottom { + replyLoader?.scrollToBottomOfLoadWindow(animated: animated) + } + } +} + +extension StoryGroupReplySheet: StoryReplyInputToolbarDelegate { + func storyReplyInputToolbarDidTapReact(_ storyReplyInputToolbar: StoryReplyInputToolbar) { + + } + + func storyReplyInputToolbarDidTapSend(_ storyReplyInputToolbar: StoryReplyInputToolbar) { + guard let messageBody = storyReplyInputToolbar.messageBody, !messageBody.text.isEmpty else { + return owsFailDebug("Unexpectedly missing message body") + } + + tryToSendTextMessage(messageBody) + } + + func tryToSendTextMessage(_ messageBody: MessageBody) { + owsAssertDebug(!messageBody.text.isEmpty) + + guard let thread = thread else { + return owsFailDebug("Unexpectedly missing thread") + } + + guard !blockingManager.isThreadBlocked(thread) else { + BlockListUIUtils.showUnblockThreadActionSheet(thread, from: self) { [weak self] isBlocked in + guard !isBlocked else { return } + self?.tryToSendTextMessage(messageBody) + } + return + } + + guard !SafetyNumberConfirmationSheet.presentIfNecessary( + addresses: thread.recipientAddresses, + confirmationText: SafetyNumberStrings.confirmSendButton, + completion: { [weak self] didConfirmIdentity in + guard didConfirmIdentity else { return } + self?.tryToSendTextMessage(messageBody) + } + ) else { return } + + let builder = TSOutgoingMessageBuilder(thread: thread) + builder.messageBody = messageBody.text + builder.bodyRanges = messageBody.ranges + builder.storyTimestamp = NSNumber(value: storyMessage.timestamp) + builder.storyAuthorAddress = storyMessage.authorAddress + let message = builder.build() + + ThreadUtil.enqueueSendAsyncWrite { [weak self] transaction in + ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequest(thread: thread, setDefaultTimerIfNecessary: false, transaction: transaction) + + message.anyInsert(transaction: transaction) + + Self.messageSenderJobQueue.add(message: message.asPreparer, transaction: transaction) + + transaction.addAsyncCompletionOnMain { + self?.replyLoader?.reload() + self?.inputToolbar.messageBody = nil + } + } + } + + func storyReplyInputToolbarDidBeginEditing(_ storyReplyInputToolbar: StoryReplyInputToolbar) { + maximizeHeight() + } + + func storyReplyInputToolbarHeightDidChange(_ storyReplyInputToolbar: StoryReplyInputToolbar) { + updateContentInsets(animated: false) + } + + func storyReplyInputToolbarMentionPickerPossibleAddresses(_ storyReplyInputToolbar: StoryReplyInputToolbar) -> [SignalServiceAddress] { + return thread?.recipientAddresses ?? [] + } +} diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift similarity index 100% rename from Signal/src/ViewControllers/HomeView/Stories/Context View/StoryGroupReplyViewItem.swift rename to Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift new file mode 100644 index 0000000000..fb6572319f --- /dev/null +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift @@ -0,0 +1,302 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import UIKit +import SignalUI + +// Coincides with Android's max text message length +let kMaxMessageBodyCharacterCount = 2000 + +protocol StoryReplyInputToolbarDelegate: AnyObject { + func storyReplyInputToolbarDidTapSend(_ storyReplyInputToolbar: StoryReplyInputToolbar) + func storyReplyInputToolbarDidTapReact(_ storyReplyInputToolbar: StoryReplyInputToolbar) + func storyReplyInputToolbarDidBeginEditing(_ storyReplyInputToolbar: StoryReplyInputToolbar) + func storyReplyInputToolbarHeightDidChange(_ storyReplyInputToolbar: StoryReplyInputToolbar) + func storyReplyInputToolbarMentionPickerPossibleAddresses(_ storyReplyInputToolbar: StoryReplyInputToolbar) -> [SignalServiceAddress] +} + +// MARK: - + +class StoryReplyInputToolbar: UIView { + + weak var delegate: StoryReplyInputToolbarDelegate? + + var messageBody: MessageBody? { + get { textView.messageBody } + set { + textView.messageBody = newValue + updateContent() + } + } + + override var bounds: CGRect { + didSet { + guard oldValue.height != bounds.height else { return } + delegate?.storyReplyInputToolbarHeightDidChange(self) + } + } + + let minTextViewHeight: CGFloat = 36 + var maxTextViewHeight: CGFloat { + // About ~4 lines in portrait and ~3 lines in landscape. + // Otherwise we risk obscuring too much of the content. + return UIDevice.current.orientation.isPortrait ? 160 : 100 + } + var textViewHeightConstraint: NSLayoutConstraint? + + // MARK: - Initializers + + init() { + super.init(frame: CGRect.zero) + + // When presenting or dismissing the keyboard, there may be a slight + // gap between the keyboard and the bottom of the input bar during + // the animation. Extend the background below the toolbar's bounds + // by this much to mask that extra space. + let backgroundExtension: CGFloat = 500 + + if UIAccessibility.isReduceTransparencyEnabled { + self.backgroundColor = .ows_black + + let extendedBackground = UIView() + addSubview(extendedBackground) + extendedBackground.autoPinWidthToSuperview() + extendedBackground.autoPinEdge(.top, to: .bottom, of: self) + extendedBackground.autoSetDimension(.height, toSize: backgroundExtension) + } else { + self.backgroundColor = .clear + + let blurEffectView = UIVisualEffectView(effect: Theme.darkThemeBarBlurEffect) + blurEffectView.layer.zPosition = -1 + addSubview(blurEffectView) + blurEffectView.autoPinWidthToSuperview() + blurEffectView.autoPinEdge(toSuperviewEdge: .top) + blurEffectView.autoPinEdge(toSuperviewEdge: .bottom, withInset: -backgroundExtension) + } + + textView.mentionDelegate = self + + let sendButton = OWSButton.sendButton(imageName: "send-solid-24") { [weak self] in + self?.didTapSend() + } + sendButtonContainer.addSubview(sendButton) + + let reactButton = OWSButton(imageName: "add-reaction-outline-24", tintColor: Theme.darkThemePrimaryColor) { [weak self] in + self?.didTapReact() + } + reactButton.autoSetDimensions(to: CGSize(square: 40)) + reactButtonContainer.addSubview(reactButton) + + for button in [sendButton, reactButton] { + button.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) + button.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) + NSLayoutConstraint.autoSetPriority(.defaultLow) { + button.autoPinEdge(toSuperviewEdge: .top) + } + + button.setContentHuggingHigh() + button.setCompressionResistanceHigh() + } + + // The input toolbar should *always* be laid out left-to-right, even when using + // a right-to-left language. The convention for messaging apps is for the send + // button to always be to the right of the input field, even in RTL layouts. + // This means, in most places you'll want to pin deliberately to left/right + // instead of leading/trailing. You'll also want to the semanticContentAttribute + // to ensure horizontal stack views layout left-to-right. + + let hStackView = UIStackView(arrangedSubviews: [ textContainer, sendButtonContainer, reactButtonContainer ]) + hStackView.isLayoutMarginsRelativeArrangement = true + hStackView.layoutMargins = UIEdgeInsets(margin: 12) + hStackView.axis = .horizontal + hStackView.alignment = .bottom + hStackView.spacing = 12 + hStackView.semanticContentAttribute = .forceLeftToRight + + addSubview(hStackView) + hStackView.autoPinEdgesToSuperviewEdges() + + textViewHeightConstraint = textView.autoSetDimension(.height, toSize: minTextViewHeight) + + textContainer.autoPinEdge(toSuperviewMargin: .top) + textContainer.autoPinEdge(toSuperviewMargin: .bottom) + + updateContent() + } + + required init?(coder aDecoder: NSCoder) { + notImplemented() + } + + // MARK: - UIView Overrides + + override var intrinsicContentSize: CGSize { + get { + // Since we have `self.autoresizingMask = UIViewAutoresizingFlexibleHeight`, we must specify + // an intrinsicContentSize. Specifying CGSize.zero causes the height to be determined by autolayout. + return CGSize.zero + } + } + + // MARK: - Subviews + + private lazy var sendButtonContainer = UIView() + private lazy var reactButtonContainer = UIView() + + lazy var textView: MentionTextView = { + let textView = buildTextView() + + textView.scrollIndicatorInsets = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 3) + textView.mentionDelegate = self + + return textView + }() + + private let placeholderText = OWSLocalizedString( + "MESSAGE_TEXT_FIELD_PLACEHOLDER", + comment: "placeholder text for the editable message field" + ) + + private lazy var placeholderTextView: UITextView = { + let placeholderTextView = buildTextView() + + placeholderTextView.text = placeholderText + placeholderTextView.isEditable = false + placeholderTextView.textContainer.maximumNumberOfLines = 1 + placeholderTextView.textContainer.lineBreakMode = .byTruncatingTail + placeholderTextView.textColor = .ows_whiteAlpha60 + + return placeholderTextView + }() + + private lazy var textContainer: UIView = { + let textContainer = UIView() + let textBubble = UIView() + textContainer.addSubview(textBubble) + let inset = (40 - minTextViewHeight) / 2 + textBubble.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 0, leading: 0, bottom: inset, trailing: 0)) + + textBubble.layer.cornerRadius = minTextViewHeight / 2 + textBubble.clipsToBounds = true + textBubble.backgroundColor = .ows_gray75 + + textBubble.addSubview(placeholderTextView) + placeholderTextView.autoPinEdgesToSuperviewEdges() + + textBubble.addSubview(textView) + textView.autoPinEdgesToSuperviewEdges() + + return textContainer + }() + + private func buildTextView() -> MentionTextView { + let textView = MentionTextView() + + textView.keyboardAppearance = Theme.darkThemeKeyboardAppearance + textView.backgroundColor = .clear + textView.tintColor = Theme.darkThemePrimaryColor + + let textViewFont = UIFont.ows_dynamicTypeBody + textView.font = textViewFont + textView.textColor = Theme.darkThemePrimaryColor + + // Check the system font size and increase text inset accordingly + // to keep the text vertically centered + textView.updateVerticalInsetsForDynamicBodyType(defaultInsets: 7) + textView.textContainerInset.left = 7 + textView.textContainerInset.right = 7 + + return textView + } + + // MARK: - Actions + + func didTapSend() { + textView.acceptAutocorrectSuggestion() + delegate?.storyReplyInputToolbarDidTapSend(self) + } + + func didTapReact() { + delegate?.storyReplyInputToolbarDidTapReact(self) + } + + // MARK: - Helpers + + private func updateContent() { + AssertIsOnMainThread() + + updateHeight(textView: textView) + + let isTextViewEmpty = textView.text.isEmptyOrNil + + reactButtonContainer.isHidden = !isTextViewEmpty + sendButtonContainer.isHidden = isTextViewEmpty + placeholderTextView.isHidden = !isTextViewEmpty + } + + private func updateHeight(textView: UITextView) { + guard let textViewHeightConstraint = textViewHeightConstraint else { + owsFailDebug("Missing constraint.") + return + } + + // compute new height assuming width is unchanged + let currentSize = textView.frame.size + let textViewHeight = clampedTextViewHeight(fixedWidth: currentSize.width) + + if textViewHeightConstraint.constant != textViewHeight { + Logger.debug("TextView height changed: \(textViewHeightConstraint.constant) -> \(textViewHeight)") + textViewHeightConstraint.constant = textViewHeight + invalidateIntrinsicContentSize() + } + } + + private func clampedTextViewHeight(fixedWidth: CGFloat) -> CGFloat { + let contentSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) + return CGFloatClamp(contentSize.height, minTextViewHeight, maxTextViewHeight) + } +} + +extension StoryReplyInputToolbar: MentionTextViewDelegate { + func textViewDidBeginTypingMention(_ textView: MentionTextView) {} + + func textViewDidEndTypingMention(_ textView: MentionTextView) {} + + func textViewMentionPickerParentView(_ textView: MentionTextView) -> UIView? { + return superview + } + + func textViewMentionPickerReferenceView(_ textView: MentionTextView) -> UIView? { + return self + } + + func textViewMentionPickerPossibleAddresses(_ textView: MentionTextView) -> [SignalServiceAddress] { + return delegate?.storyReplyInputToolbarMentionPickerPossibleAddresses(self) ?? [] + } + + func textView(_ textView: MentionTextView, didDeleteMention mention: Mention) {} + + func textView(_ textView: MentionTextView, shouldResolveMentionForAddress address: SignalServiceAddress) -> Bool { + return textViewMentionPickerPossibleAddresses(textView).contains(address) + } + + func textViewMentionStyle(_ textView: MentionTextView) -> Mention.Style { + return .groupReply + } + + public func textViewDidChange(_ textView: UITextView) { + updateHeight(textView: textView) + updateContent() + } + + public func textViewDidBeginEditing(_ textView: UITextView) { + delegate?.storyReplyInputToolbarDidBeginEditing(self) + updateContent() + } + + public func textViewDidEndEditing(_ textView: UITextView) { + updateContent() + } +} diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 7c078e6a12..ed79b73f6d 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @@ -47,6 +47,11 @@ NS_ASSUME_NONNULL_BEGIN transaction:(SDSAnyWriteTransaction *)transaction NS_SWIFT_NAME(addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer(thread:transaction:)); ++ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequest:(TSThread *)thread + setDefaultTimerIfNecessary:(BOOL)setDefaultTimerIfNecessary + transaction:(SDSAnyWriteTransaction *)transaction + NS_SWIFT_NAME(addThreadToProfileWhitelistIfEmptyOrPendingRequest(thread:setDefaultTimerIfNecessary:transaction:)); + #pragma mark - Delete Content + (void)deleteAllContent; diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index e445a89bd3..22a8e43913 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -182,6 +182,15 @@ NS_ASSUME_NONNULL_BEGIN + (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:(TSThread *)thread transaction:(SDSAnyWriteTransaction *)transaction +{ + return [self addThreadToProfileWhitelistIfEmptyOrPendingRequest:thread + setDefaultTimerIfNecessary:YES + transaction:transaction]; +} + ++ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequest:(TSThread *)thread + setDefaultTimerIfNecessary:(BOOL)setDefaultTimerIfNecessary + transaction:(SDSAnyWriteTransaction *)transaction { OWSAssertDebug(thread); @@ -192,7 +201,7 @@ NS_ASSUME_NONNULL_BEGIN [GRDBThreadFinder shouldSetDefaultDisappearingMessageTimerWithThread:thread transaction:transaction.unwrapGrdbRead]; - if (needsDefaultTimerSet) { + if (needsDefaultTimerSet && setDefaultTimerIfNecessary) { OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration applyToken:defaultTimerToken toThread:thread transaction:transaction]; diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index f5421c3d45..0d06331ac6 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -116,7 +116,9 @@ NS_ASSUME_NONNULL_BEGIN messageSticker:transcript.messageSticker isViewOnceMessage:transcript.isViewOnceMessage changeActionsProtoData:nil - additionalRecipients:nil] build]; + additionalRecipients:nil + storyAuthorAddress:nil + storyTimestamp:nil] build]; SignalServiceAddress *_Nullable localAddress = self.tsAccountManager.localAddress; if (localAddress == nil) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index bc8257ab8e..7d26f58464 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -1089,6 +1089,21 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; } } + // Story Context + if (self.storyTimestamp && self.storyAuthorUuidString) { + SSKProtoDataMessageStoryContextBuilder *storyContextBuilder = [SSKProtoDataMessageStoryContext builder]; + [storyContextBuilder setAuthorUuid:self.storyAuthorUuidString]; + [storyContextBuilder setSentTimestamp:self.storyTimestamp.longLongValue]; + + NSError *error; + SSKProtoDataMessageStoryContext *_Nullable storyContext = [storyContextBuilder buildAndReturnError:&error]; + if (error || !storyContext) { + OWSFailDebug(@"Could not build storyContext protobuf: %@.", error); + } else { + [builder setStoryContext:storyContext]; + } + } + [builder setExpireTimer:self.expiresInSeconds]; // Group Messages diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift index 86adac63fb..43e2fa99f5 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift @@ -36,7 +36,9 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { messageSticker: MessageSticker? = nil, isViewOnceMessage: Bool = false, changeActionsProtoData: Data? = nil, - additionalRecipients: [SignalServiceAddress]? = nil) { + additionalRecipients: [SignalServiceAddress]? = nil, + storyAuthorAddress: SignalServiceAddress? = nil, + storyTimestamp: UInt64? = nil) { super.init(thread: thread, timestamp: timestamp, @@ -49,7 +51,9 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { contactShare: contactShare, linkPreview: linkPreview, messageSticker: messageSticker, - isViewOnceMessage: isViewOnceMessage) + isViewOnceMessage: isViewOnceMessage, + storyAuthorAddress: storyAuthorAddress, + storyTimestamp: storyTimestamp) self.isVoiceMessage = isVoiceMessage self.groupMetaMessage = groupMetaMessage @@ -88,7 +92,9 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { messageSticker: MessageSticker?, isViewOnceMessage: Bool, changeActionsProtoData: Data?, - additionalRecipients: [SignalServiceAddress]?) -> TSOutgoingMessageBuilder { + additionalRecipients: [SignalServiceAddress]?, + storyAuthorAddress: SignalServiceAddress?, + storyTimestamp: NSNumber?) -> TSOutgoingMessageBuilder { return TSOutgoingMessageBuilder(thread: thread, timestamp: timestamp, messageBody: messageBody, @@ -104,7 +110,9 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { messageSticker: messageSticker, isViewOnceMessage: isViewOnceMessage, changeActionsProtoData: changeActionsProtoData, - additionalRecipients: additionalRecipients) + additionalRecipients: additionalRecipients, + storyAuthorAddress: storyAuthorAddress, + storyTimestamp: storyTimestamp?.uint64Value) } private var hasBuilt = false diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index 21e44a0cfd..0a56c12ad5 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -1519,6 +1519,7 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { AND \(interactionColumn: .errorType) IN (\(errorMessageTypes.map { "\($0.rawValue)" }.joined(separator: ","))) ) OR \(interactionColumn: .recordType) IN (\(interactionTypes.map { "\($0.rawValue)" }.joined(separator: ","))) ) + \(Self.filterStoryRepliesClause(for: .excludeGroupReplies)) LIMIT 1 ) """ diff --git a/SignalServiceKit/src/TestUtils/Factories.swift b/SignalServiceKit/src/TestUtils/Factories.swift index 81d3536e2c..b7a7a17c99 100644 --- a/SignalServiceKit/src/TestUtils/Factories.swift +++ b/SignalServiceKit/src/TestUtils/Factories.swift @@ -139,7 +139,9 @@ public class OutgoingMessageFactory: NSObject, Factory { messageSticker: messageStickerBuilder(), isViewOnceMessage: isViewOnceMessageBuilder(), changeActionsProtoData: changeActionsProtoDataBuilder(), - additionalRecipients: additionalRecipientsBuilder()).build() + additionalRecipients: additionalRecipientsBuilder(), + storyAuthorAddress: storyAuthorAddressBuilder(), + storyTimestamp: storyTimestampBuilder()).build() } @objc @@ -234,6 +236,16 @@ public class OutgoingMessageFactory: NSObject, Factory { return nil } + @objc + public var storyAuthorAddressBuilder: () -> SignalServiceAddress? = { + return nil + } + + @objc + public var storyTimestampBuilder: () -> NSNumber? = { + return nil + } + // MARK: Delivery Receipts @objc diff --git a/SignalUI/Views/InputAccessoryViewPlaceholder.swift b/SignalUI/Views/InputAccessoryViewPlaceholder.swift index 7cc0981db6..fbafa3e81b 100644 --- a/SignalUI/Views/InputAccessoryViewPlaceholder.swift +++ b/SignalUI/Views/InputAccessoryViewPlaceholder.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -36,7 +36,7 @@ public class InputAccessoryViewPlaceholder: UIView { } @objc - weak var referenceView: UIView? + public weak var referenceView: UIView? private var visibleKeyboardHeight: CGFloat { guard var keyboardFrame = transitioningKeyboardFrame ?? superview?.frame else { return 0 } diff --git a/SignalUI/Views/Mentions/MentionPicker.swift b/SignalUI/Views/Mentions/MentionPicker.swift index 6db1b80105..b90ae57255 100644 --- a/SignalUI/Views/Mentions/MentionPicker.swift +++ b/SignalUI/Views/Mentions/MentionPicker.swift @@ -159,6 +159,22 @@ class MentionPicker: UIView { if style == .composingAttachment { tableView.backgroundColor = UIColor.ows_gray95 hairlineView.backgroundColor = .ows_gray65 + } else if style == .groupReply { + blurView?.removeFromSuperview() + blurView = nil + + if UIAccessibility.isReduceTransparencyEnabled { + tableView.backgroundColor = Theme.darkThemeBackgroundColor + } else { + tableView.backgroundColor = .clear + + let blurView = UIVisualEffectView(effect: Theme.darkThemeBarBlurEffect) + self.blurView = blurView + insertSubview(blurView, belowSubview: tableView) + blurView.autoPinEdgesToSuperviewEdges() + } + + hairlineView.backgroundColor = .ows_gray75 } else { blurView?.removeFromSuperview() blurView = nil @@ -330,7 +346,7 @@ private class MentionableUserCell: UITableViewCell { } func configure(with mentionableUser: MentionPicker.MentionableUser, style: Mention.Style) { - if style == .composingAttachment { + if [.composingAttachment, .groupReply].contains(style) { displayNameLabel.textColor = Theme.darkThemePrimaryColor usernameLabel.textColor = Theme.darkThemeSecondaryTextAndIconColor selectedBackgroundView?.backgroundColor = UIColor.white.withAlphaComponent(0.2) From 9477a10ea9346a847c3caf6eb66eb1b48d39d9bc Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 13:40:57 -0700 Subject: [PATCH 11/20] Add rendering support for deleted replies --- .../StoryGroupReplyCell.swift | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift index 0193c859cf..0ffb686375 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift @@ -157,13 +157,22 @@ class StoryGroupReplyCell: UITableViewCell { } func configureTextAndTimestamp(for item: StoryGroupReplyViewItem) { - guard let displayableText = item.displayableText else { return } - - let messageText = displayableText.displayAttributedText.styled( - with: .font(.ows_dynamicTypeBodyClamped), - .color(.ows_gray05), - .alignment(displayableText.displayTextNaturalAlignment) - ) + guard let messageText: NSAttributedString = { + if let displayableText = item.displayableText { + return displayableText.displayAttributedText.styled( + with: .font(.ows_dynamicTypeBodyClamped), + .color(.ows_gray05), + .alignment(displayableText.displayTextNaturalAlignment) + ) + } else if item.wasRemotelyDeleted { + return NSLocalizedString("THIS_MESSAGE_WAS_DELETED", comment: "text indicating the message was remotely deleted").styled( + with: .font(UIFont.ows_dynamicTypeBodyClamped.ows_italic), + .color(.ows_gray05) + ) + } else { + return nil + } + }() else { return } switch cellType { case .standalone, .bottom: @@ -182,7 +191,7 @@ class StoryGroupReplyCell: UITableViewCell { let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageMeasurement.lastLineRect.width let textDirectionMatchesAppDirection: Bool - switch displayableText.displayTextNaturalAlignment { + switch item.displayableText?.displayTextNaturalAlignment ?? .natural { case .left: textDirectionMatchesAppDirection = !CurrentAppContext().isRTL case .right: From 9477eea53098ccd015c88842324f7527f43b37ae Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 13:42:20 -0700 Subject: [PATCH 12/20] Fix placeholder --- .../Stories/Group Reply Sheet/StoryReplyInputToolbar.swift | 4 ++-- Signal/translations/en.lproj/Localizable.strings | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift index fb6572319f..a35b6ade96 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryReplyInputToolbar.swift @@ -155,8 +155,8 @@ class StoryReplyInputToolbar: UIView { }() private let placeholderText = OWSLocalizedString( - "MESSAGE_TEXT_FIELD_PLACEHOLDER", - comment: "placeholder text for the editable message field" + "STORY_REPLY_TEXT_FIELD_PLACEHOLDER", + comment: "placeholder text for replying to a story" ) private lazy var placeholderTextView: UITextView = { diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 12c64935a4..dd144bc013 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -6115,6 +6115,9 @@ /* Author text when the local user author's a story reply */ "STORY_REPLY_YOU_AUTHOR" = "You"; +/* placeholder text for replying to a story */ +"STORY_REPLY_TEXT_FIELD_PLACEHOLDER" = "Reply"; + /* Title for the 'Badges' button in sustainer view. */ "SUBSCRIBER_BADGES" = "Badges"; From 4c6aa8de2df448e80ac0d387f29643fe3355e362 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 14:05:04 -0700 Subject: [PATCH 13/20] Add storyReactionEmoji column to TSMessage --- SignalServiceKit/Resources/schema.sql | 1 + SignalServiceKit/src/Messages/Interactions/TSMessage.h | 1 + SignalServiceKit/src/Messages/Interactions/TSMessage.m | 2 +- SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/SignalServiceKit/Resources/schema.sql b/SignalServiceKit/Resources/schema.sql index 2c3a8451c7..4eff687da6 100644 --- a/SignalServiceKit/Resources/schema.sql +++ b/SignalServiceKit/Resources/schema.sql @@ -118,6 +118,7 @@ CREATE ,"storyAuthorUuidString" TEXT ,"storyTimestamp" INTEGER ,"isGroupStoryReply" BOOLEAN + ,"storyReactionEmoji" TEXT ) ; diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index 3b91040d28..fe66399205 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -52,6 +52,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) NSString *storyAuthorUuidString; @property (nonatomic, readonly) BOOL isGroupStoryReply; @property (nonatomic, readonly) BOOL isStoryReply; +@property (nonatomic, readonly, nullable) NSString *storyReactionEmoji; - (instancetype)initWithUniqueId:(NSString *)uniqueId timestamp:(uint64_t)timestamp diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 3966848881..b01741b630 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -834,7 +834,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; - (BOOL)hasRenderableStoryReplyContent { - return self.body.length > 0; + return self.body.length > 0 || self.storyReactionEmoji.isSingleEmoji; } #pragma mark - View Once diff --git a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift index 5eeab7179d..60b7432444 100644 --- a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift +++ b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift @@ -1572,6 +1572,7 @@ public class GRDBSchemaMigrator: NSObject { table.add(column: "storyAuthorUuidString", .text) table.add(column: "storyTimestamp", .integer) table.add(column: "isGroupStoryReply", .boolean) + table.add(column: "storyReactionEmoji", .text) } try db.execute(sql: "UPDATE model_TSInteraction SET isGroupStoryReply = 0") From 014fe25b16bb3407416205997bd2d19a48885b31 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 14:05:21 -0700 Subject: [PATCH 14/20] sds codegen --- .../sds_config/sds-property_order.json | 1 + ...ngConfigurationUpdateInfoMessage+SDS.swift | 3 +- ...sappearingConfigurationUpdateInfoMessage.h | 3 +- ...sappearingConfigurationUpdateInfoMessage.m | 2 + ...WSVerificationStateChangeMessage+SDS.swift | 3 +- .../OWSVerificationStateChangeMessage.h | 3 +- .../OWSVerificationStateChangeMessage.m | 2 + .../Interactions/TSErrorMessage+SDS.swift | 3 +- .../Messages/Interactions/TSErrorMessage.h | 3 +- .../Messages/Interactions/TSErrorMessage.m | 2 + .../Interactions/TSIncomingMessage+SDS.swift | 3 +- .../Messages/Interactions/TSIncomingMessage.h | 3 +- .../Messages/Interactions/TSIncomingMessage.m | 2 + .../Interactions/TSInfoMessage+SDS.swift | 3 +- .../src/Messages/Interactions/TSInfoMessage.h | 3 +- .../src/Messages/Interactions/TSInfoMessage.m | 2 + .../Interactions/TSInteraction+SDS.swift | 74 ++++++++++++++++++- .../Messages/Interactions/TSMessage+SDS.swift | 3 +- .../src/Messages/Interactions/TSMessage.h | 3 +- .../src/Messages/Interactions/TSMessage.m | 2 + .../Interactions/TSOutgoingMessage+SDS.swift | 3 +- .../Messages/Interactions/TSOutgoingMessage.h | 3 +- .../Messages/Interactions/TSOutgoingMessage.m | 2 + .../TSUnreadIndicatorInteraction+SDS.swift | 3 +- ...TSInvalidIdentityKeyErrorMessage+SDS.swift | 3 +- ...IdentityKeyReceivingErrorMessage+SDS.swift | 3 +- ...SInvalidIdentityKeyReceivingErrorMessage.h | 3 +- ...SInvalidIdentityKeyReceivingErrorMessage.m | 2 + ...idIdentityKeySendingErrorMessage+SDS.swift | 3 +- .../TSInvalidIdentityKeySendingErrorMessage.h | 3 +- .../TSInvalidIdentityKeySendingErrorMessage.m | 2 + .../OWSAddToContactsOfferMessage+SDS.swift | 3 +- ...ddToProfileWhitelistOfferMessage+SDS.swift | 3 +- .../Messages/OWSGroupCallMessage+SDS.swift | 3 +- ...RecoverableDecryptionPlaceholder+SDS.swift | 3 +- ...SUnknownContactBlockOfferMessage+SDS.swift | 3 +- ...OWSUnknownProtocolVersionMessage+SDS.swift | 3 +- .../OWSUnknownProtocolVersionMessage.h | 3 +- .../OWSUnknownProtocolVersionMessage.m | 2 + .../OWSOutgoingPaymentMessage+SDS.swift | 3 +- .../Payments/OWSOutgoingPaymentMessage.h | 3 +- .../Payments/OWSOutgoingPaymentMessage.m | 2 + .../src/Messages/TSCall+SDS.swift | 3 +- 43 files changed, 155 insertions(+), 32 deletions(-) diff --git a/Scripts/sds_codegen/sds_config/sds-property_order.json b/Scripts/sds_codegen/sds_config/sds-property_order.json index 4ef3ec762c..a563f341cc 100644 --- a/Scripts/sds_codegen/sds_config/sds-property_order.json +++ b/Scripts/sds_codegen/sds_config/sds-property_order.json @@ -115,6 +115,7 @@ "InteractionRecord.storedMessageState": 45, "InteractionRecord.storedShouldStartExpireTimer": 46, "InteractionRecord.storyAuthorUuidString": 65, + "InteractionRecord.storyReactionEmoji": 68, "InteractionRecord.storyTimestamp": 66, "InteractionRecord.timestamp": 2, "InteractionRecord.uniqueThreadId": 3, diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift index cd84b929d7..e76bfc02ca 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSDisappearingConfigurationUpdateInfoMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h index 137fef4b1a..dad78a215e 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h @@ -75,6 +75,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -86,7 +87,7 @@ NS_ASSUME_NONNULL_BEGIN configurationIsEnabled:(BOOL)configurationIsEnabled createdByRemoteName:(nullable NSString *)createdByRemoteName createdInExistingGroup:(BOOL)createdInExistingGroup -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:configurationDurationSeconds:configurationIsEnabled:createdByRemoteName:createdInExistingGroup:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:configurationDurationSeconds:configurationIsEnabled:createdByRemoteName:createdInExistingGroup:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index 77623fecef..f7e4f4ff96 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -75,6 +75,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -108,6 +109,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift index 927cd3d6ea..cca8b9a6e6 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSVerificationStateChangeMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h index 44981a7b42..39746bd9a1 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h @@ -75,6 +75,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -85,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN isLocalChange:(BOOL)isLocalChange recipientAddress:(SignalServiceAddress *)recipientAddress verificationState:(OWSVerificationState)verificationState -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:isLocalChange:recipientAddress:verificationState:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:isLocalChange:recipientAddress:verificationState:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m index bedbb4c48c..01fd066128 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m @@ -69,6 +69,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -101,6 +102,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift index 46ba7a68b2..c36d16688c 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift @@ -128,7 +128,8 @@ class TSErrorMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index 8518467739..b03a30979b 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -96,6 +96,7 @@ extern NSUInteger TSErrorMessageSchemaVersion; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -103,7 +104,7 @@ extern NSUInteger TSErrorMessageSchemaVersion; recipientAddress:(nullable SignalServiceAddress *)recipientAddress sender:(nullable SignalServiceAddress *)sender wasIdentityVerified:(BOOL)wasIdentityVerified -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 8141b188ff..38f497a8e8 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -144,6 +144,7 @@ NSUInteger TSErrorMessageSchemaVersion = 2; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -173,6 +174,7 @@ NSUInteger TSErrorMessageSchemaVersion = 2; quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift index 082ee62e3f..769e47ba1c 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift @@ -128,7 +128,8 @@ class TSIncomingMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h index ca49d2cdab..156264ae34 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.h @@ -75,6 +75,7 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(incomingMessageWithBuilder:)); quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted authorPhoneNumber:(nullable NSString *)authorPhoneNumber @@ -86,7 +87,7 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(incomingMessageWithBuilder:)); sourceDeviceId:(unsigned int)sourceDeviceId viewed:(BOOL)viewed wasReceivedByUD:(BOOL)wasReceivedByUD -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:authorPhoneNumber:authorUUID:read:serverDeliveryTimestamp:serverGuid:serverTimestamp:sourceDeviceId:viewed:wasReceivedByUD:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:authorPhoneNumber:authorUUID:read:serverDeliveryTimestamp:serverGuid:serverTimestamp:sourceDeviceId:viewed:wasReceivedByUD:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m index 63d98a3dda..78e68f7f60 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m @@ -99,6 +99,7 @@ const NSUInteger TSIncomingMessageSchemaVersion = 1; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted authorPhoneNumber:(nullable NSString *)authorPhoneNumber @@ -132,6 +133,7 @@ const NSUInteger TSIncomingMessageSchemaVersion = 1; quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift index b2592f5ca5..4a40475276 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift @@ -128,7 +128,8 @@ class TSInfoMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h index 5c8520ad39..ebfcfe02f8 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.h @@ -117,6 +117,7 @@ extern InfoMessageUserInfoKey const InfoMessageUserInfoKeyChangePhoneNumberNew; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -124,7 +125,7 @@ extern InfoMessageUserInfoKey const InfoMessageUserInfoKeyChangePhoneNumberNew; messageType:(TSInfoMessageType)messageType read:(BOOL)read unregisteredAddress:(nullable SignalServiceAddress *)unregisteredAddress -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m index dacfdbd33a..58ac724f04 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m @@ -149,6 +149,7 @@ NSUInteger TSInfoMessageSchemaVersion = 2; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -178,6 +179,7 @@ NSUInteger TSInfoMessageSchemaVersion = 2; quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift index 0d87366779..7082c6b7d3 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift @@ -97,6 +97,7 @@ public struct InteractionRecord: SDSRecord { public let storyAuthorUuidString: String? public let storyTimestamp: UInt64? public let isGroupStoryReply: Bool? + public let storyReactionEmoji: String? public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { case id @@ -169,6 +170,7 @@ public struct InteractionRecord: SDSRecord { case storyAuthorUuidString case storyTimestamp case isGroupStoryReply + case storyReactionEmoji } public static func columnName(_ column: InteractionRecord.CodingKeys, fullyQualified: Bool = false) -> String { @@ -262,6 +264,7 @@ public extension InteractionRecord { storyAuthorUuidString = row[67] storyTimestamp = row[68] isGroupStoryReply = row[69] + storyReactionEmoji = row[70] } } @@ -318,6 +321,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -351,6 +355,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -387,6 +392,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -420,6 +426,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -456,6 +463,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -493,6 +501,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -559,6 +568,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -607,6 +617,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -652,6 +663,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -685,6 +697,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -721,6 +734,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -754,6 +768,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -790,6 +805,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -826,6 +842,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -864,6 +881,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -903,6 +921,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -967,6 +986,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -1000,6 +1020,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -1036,6 +1057,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let authorPhoneNumber: String? = record.authorPhoneNumber @@ -1069,6 +1091,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, authorPhoneNumber: authorPhoneNumber, @@ -1109,6 +1132,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -1142,6 +1166,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -1193,6 +1218,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -1226,6 +1252,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -1262,6 +1289,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -1297,6 +1325,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -1335,6 +1364,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") guard let errorType: TSErrorMessageType = record.errorType else { @@ -1371,6 +1401,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -1409,6 +1440,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") @@ -1433,6 +1465,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted) @@ -1464,6 +1497,7 @@ extension TSInteraction { let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage") let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer") let storyAuthorUuidString: String? = record.storyAuthorUuidString + let storyReactionEmoji: String? = record.storyReactionEmoji let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) }) let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted") let customMessage: String? = record.customMessage @@ -1506,6 +1540,7 @@ extension TSInteraction { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -1737,6 +1772,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -1823,6 +1859,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -1925,6 +1962,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -1972,6 +2010,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2071,6 +2110,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2129,6 +2169,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2225,6 +2266,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2292,6 +2334,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2387,6 +2430,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2444,6 +2488,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2541,6 +2586,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2594,6 +2640,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2687,6 +2734,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2740,6 +2788,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2833,6 +2882,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let customMessage: String? = modelToCopy.customMessage @@ -2886,6 +2936,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, customMessage: customMessage, @@ -2979,6 +3030,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let authorPhoneNumber: String? = modelToCopy.authorPhoneNumber @@ -3012,6 +3064,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, authorPhoneNumber: authorPhoneNumber, @@ -3109,6 +3162,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3166,6 +3220,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3261,6 +3316,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3316,6 +3372,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3411,6 +3468,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3464,6 +3522,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3557,6 +3616,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3610,6 +3670,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3703,6 +3764,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3756,6 +3818,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3849,6 +3912,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted let errorType: TSErrorMessageType = modelToCopy.errorType @@ -3902,6 +3966,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted, errorType: errorType, @@ -3995,6 +4060,7 @@ extension TSInteraction: DeepCopyable { } let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString + let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted @@ -4019,6 +4085,7 @@ extension TSInteraction: DeepCopyable { quotedMessage: quotedMessage, storedShouldStartExpireTimer: storedShouldStartExpireTimer, storyAuthorUuidString: storyAuthorUuidString, + storyReactionEmoji: storyReactionEmoji, storyTimestamp: storyTimestamp, wasRemotelyDeleted: wasRemotelyDeleted) } @@ -4180,6 +4247,7 @@ extension TSInteractionSerializer { static var storyAuthorUuidStringColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyAuthorUuidString", columnType: .unicodeString, isOptional: true) } static var storyTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyTimestamp", columnType: .int64, isOptional: true) } static var isGroupStoryReplyColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isGroupStoryReply", columnType: .int, isOptional: true) } + static var storyReactionEmojiColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyReactionEmoji", columnType: .unicodeString, isOptional: true) } // TODO: We should decide on a naming convention for // tables that store models. @@ -4256,7 +4324,8 @@ extension TSInteractionSerializer { serverGuidColumn, storyAuthorUuidStringColumn, storyTimestampColumn, - isGroupStoryReplyColumn + isGroupStoryReplyColumn, + storyReactionEmojiColumn ]) } } @@ -4730,8 +4799,9 @@ class TSInteractionSerializer: SDSSerializer { let storyAuthorUuidString: String? = nil let storyTimestamp: UInt64? = nil let isGroupStoryReply: Bool? = nil + let storyReactionEmoji: String? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift index 2474faa20b..1a29352f8d 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift @@ -128,7 +128,8 @@ class TSMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index fe66399205..c195bb59ba 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -101,9 +101,10 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(messageWithBuilder:)); quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index b01741b630..f75a79ef6d 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -130,6 +130,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted { @@ -159,6 +160,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _quotedMessage = quotedMessage; _storedShouldStartExpireTimer = storedShouldStartExpireTimer; _storyAuthorUuidString = storyAuthorUuidString; + _storyReactionEmoji = storyReactionEmoji; _storyTimestamp = storyTimestamp; _wasRemotelyDeleted = wasRemotelyDeleted; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift index 992f4cd459..225ff49178 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift @@ -128,7 +128,8 @@ class TSOutgoingMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index 90d5a386de..3060316d5b 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -139,6 +139,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -152,7 +153,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { mostRecentFailureText:(nullable NSString *)mostRecentFailureText recipientAddressStates:(nullable NSDictionary *)recipientAddressStates storedMessageState:(TSOutgoingMessageState)storedMessageState -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 7d26f58464..3f662e28ab 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -143,6 +143,7 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -178,6 +179,7 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted]; diff --git a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift index 6bc15afa44..049142dc90 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift @@ -128,7 +128,8 @@ class TSUnreadIndicatorInteractionSerializer: SDSSerializer { let storyAuthorUuidString: String? = nil let storyTimestamp: UInt64? = nil let isGroupStoryReply: Bool? = nil + let storyReactionEmoji: String? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift index 94e61d8bef..20d441f010 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift @@ -128,7 +128,8 @@ class TSInvalidIdentityKeyErrorMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift index 185aab21fa..7349681cf0 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift @@ -128,7 +128,8 @@ class TSInvalidIdentityKeyReceivingErrorMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h index 0988e543ba..7a67caaad8 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h @@ -46,6 +46,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -55,7 +56,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage wasIdentityVerified:(BOOL)wasIdentityVerified authorId:(NSString *)authorId envelopeData:(nullable NSData *)envelopeData -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:authorId:envelopeData:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:authorId:envelopeData:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 20be866953..33db1b1873 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -107,6 +107,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -138,6 +139,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted errorType:errorType diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift index 0307bf616d..92ec18130f 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift @@ -128,7 +128,8 @@ class TSInvalidIdentityKeySendingErrorMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h index 76eaa5d30e..ed3c03363a 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h @@ -45,6 +45,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -54,7 +55,7 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : wasIdentityVerified:(BOOL)wasIdentityVerified messageId:(NSString *)messageId preKeyBundle:(PreKeyBundle *)preKeyBundle -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:messageId:preKeyBundle:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:messageId:preKeyBundle:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m index f6a1a676cb..27aa0743b7 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m @@ -60,6 +60,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted errorType:(TSErrorMessageType)errorType @@ -91,6 +92,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted errorType:errorType diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift index f32e03036e..fc6c562958 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSAddToContactsOfferMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift index 3314261cee..4ffcb5fad4 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSAddToProfileWhitelistOfferMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift index e2c86ba527..bf5dc3f020 100644 --- a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSGroupCallMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = nil let storyTimestamp: UInt64? = nil let isGroupStoryReply: Bool? = nil + let storyReactionEmoji: String? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift index 9119fa237b..16f0a40b3f 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift @@ -128,7 +128,8 @@ class OWSRecoverableDecryptionPlaceholderSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift index e2f23837d7..0455a2f5b9 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSUnknownContactBlockOfferMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift index be6d553ae6..4dd5a8acb6 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSUnknownProtocolVersionMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h index fd48d188ff..e3eaf9aab0 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.h @@ -74,6 +74,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -83,7 +84,7 @@ NS_ASSUME_NONNULL_BEGIN unregisteredAddress:(nullable SignalServiceAddress *)unregisteredAddress protocolVersion:(NSUInteger)protocolVersion sender:(nullable SignalServiceAddress *)sender -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:protocolVersion:sender:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:infoMessageUserInfo:messageType:read:unregisteredAddress:protocolVersion:sender:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m index c90f9088ac..e905b37722 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage.m @@ -86,6 +86,7 @@ NSUInteger const OWSUnknownProtocolVersionMessageSchemaVersion = 1; quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -117,6 +118,7 @@ NSUInteger const OWSUnknownProtocolVersionMessageSchemaVersion = 1; quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift index 5b36d041f5..e4218e52cf 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift @@ -128,7 +128,8 @@ class OWSOutgoingPaymentMessageSerializer: SDSSerializer { let storyAuthorUuidString: String? = model.storyAuthorUuidString let storyTimestamp: UInt64? = archiveOptionalNSNumber(model.storyTimestamp, conversion: { $0.uint64Value }) let isGroupStoryReply: Bool? = model.isGroupStoryReply + let storyReactionEmoji: String? = model.storyReactionEmoji - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h index 3389695783..cdc3cddb3d 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.h @@ -51,6 +51,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -67,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN paymentCancellation:(nullable TSPaymentCancellation *)paymentCancellation paymentNotification:(nullable TSPaymentNotification *)paymentNotification paymentRequest:(nullable TSPaymentRequest *)paymentRequest -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:paymentCancellation:paymentNotification:paymentRequest:)); +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isGroupStoryReply:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:storyAuthorUuidString:storyReactionEmoji:storyTimestamp:wasRemotelyDeleted:customMessage:groupMetaMessage:hasLegacyMessageState:hasSyncedTranscript:isFromLinkedDevice:isVoiceMessage:legacyMessageState:legacyWasDelivered:mostRecentFailureText:recipientAddressStates:storedMessageState:paymentCancellation:paymentNotification:paymentRequest:)); // clang-format on diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m index f10cbfe81f..5966cc3775 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage.m @@ -65,6 +65,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:(nullable TSQuotedMessage *)quotedMessage storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer storyAuthorUuidString:(nullable NSString *)storyAuthorUuidString + storyReactionEmoji:(nullable NSString *)storyReactionEmoji storyTimestamp:(nullable NSNumber *)storyTimestamp wasRemotelyDeleted:(BOOL)wasRemotelyDeleted customMessage:(nullable NSString *)customMessage @@ -103,6 +104,7 @@ NS_ASSUME_NONNULL_BEGIN quotedMessage:quotedMessage storedShouldStartExpireTimer:storedShouldStartExpireTimer storyAuthorUuidString:storyAuthorUuidString + storyReactionEmoji:storyReactionEmoji storyTimestamp:storyTimestamp wasRemotelyDeleted:wasRemotelyDeleted customMessage:customMessage diff --git a/SignalServiceKit/src/Messages/TSCall+SDS.swift b/SignalServiceKit/src/Messages/TSCall+SDS.swift index 4a3fc2092c..8294cad37e 100644 --- a/SignalServiceKit/src/Messages/TSCall+SDS.swift +++ b/SignalServiceKit/src/Messages/TSCall+SDS.swift @@ -128,7 +128,8 @@ class TSCallSerializer: SDSSerializer { let storyAuthorUuidString: String? = nil let storyTimestamp: UInt64? = nil let isGroupStoryReply: Bool? = nil + let storyReactionEmoji: String? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji) } } From 0e54e5cf118d613a64dcb1da1c6864c7932365d6 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 15:06:05 -0700 Subject: [PATCH 15/20] Add support for incoming reaction story replies --- .../StoryGroupReplyCell.swift | 46 +++++--- .../StoryGroupReplyViewItem.swift | 9 +- .../translations/en.lproj/Localizable.strings | 3 + .../src/Devices/OWSRecordTranscriptJob.m | 5 +- .../OWSIncomingSentMessageTranscript.h | 4 +- .../OWSIncomingSentMessageTranscript.m | 13 ++- .../TSIncomingMessage+Builder.swift | 12 +- .../Interactions/TSMessage+Builder.swift | 6 +- .../src/Messages/Interactions/TSMessage.m | 1 + .../Interactions/TSOutgoingMessage.swift | 12 +- .../src/Messages/OWSMessageManager.m | 7 +- .../Messages/Reactions/ReactionManager.swift | 103 +++++++++++------- .../src/TestUtils/Factories.swift | 16 ++- 13 files changed, 166 insertions(+), 71 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift index 0ffb686375..9082623b55 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift @@ -18,10 +18,9 @@ class StoryGroupReplyCell: UITableViewCell { label.font = UIFont.ows_dynamicTypeFootnoteClamped.ows_semibold return label }() - lazy var timestampLabel: UILabel = { + lazy var reactionLabel: UILabel = { let label = UILabel() - label.font = UIFont.ows_dynamicTypeCaption1Clamped - label.textColor = .ows_gray25 + label.font = .systemFont(ofSize: 28) label.textAlignment = .trailing return label }() @@ -121,7 +120,17 @@ class StoryGroupReplyCell: UITableViewCell { vStack.addArrangedSubview(messageLabel) case .reaction: - break + hStack.alignment = .center + hStack.layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 6) + avatarView.autoSetDimensions(to: CGSize(square: 28)) + hStack.addArrangedSubview(avatarView) + hStack.addArrangedSubview(.spacer(withWidth: 20)) + hStack.addArrangedSubview(vStack) + hStack.addArrangedSubview(.hStretchingSpacer()) + hStack.addArrangedSubview(reactionLabel) + + vStack.addArrangedSubview(authorNameLabel) + vStack.addArrangedSubview(messageLabel) } contentView.addSubview(hStack) @@ -150,7 +159,12 @@ class StoryGroupReplyCell: UITableViewCell { case .middle: break case .reaction: - break + authorNameLabel.textColor = item.authorColor + authorNameLabel.text = item.authorDisplayName + reactionLabel.text = item.reactionEmoji + avatarView.updateWithSneakyTransactionIfNecessary { config in + config.dataSource = .address(item.authorAddress) + } } configureTextAndTimestamp(for: item) @@ -158,24 +172,30 @@ class StoryGroupReplyCell: UITableViewCell { func configureTextAndTimestamp(for item: StoryGroupReplyViewItem) { guard let messageText: NSAttributedString = { - if let displayableText = item.displayableText { + if item.wasRemotelyDeleted { + return NSLocalizedString("THIS_MESSAGE_WAS_DELETED", comment: "text indicating the message was remotely deleted").styled( + with: .font(UIFont.ows_dynamicTypeBodyClamped.ows_italic), + .color(.ows_gray05) + ) + } else if cellType == .reaction { + return NSLocalizedString("STORY_REPLY_REACTION", comment: "Text indicating a story has been reacted to").styled( + with: .font(.ows_dynamicTypeBodyClamped), + .color(.ows_gray05), + .alignment(.natural) + ) + } else if let displayableText = item.displayableText { return displayableText.displayAttributedText.styled( with: .font(.ows_dynamicTypeBodyClamped), .color(.ows_gray05), .alignment(displayableText.displayTextNaturalAlignment) ) - } else if item.wasRemotelyDeleted { - return NSLocalizedString("THIS_MESSAGE_WAS_DELETED", comment: "text indicating the message was remotely deleted").styled( - with: .font(UIFont.ows_dynamicTypeBodyClamped.ows_italic), - .color(.ows_gray05) - ) } else { return nil } }() else { return } switch cellType { - case .standalone, .bottom: + case .standalone, .bottom, .reaction: // Append timestamp to attributed text let timestampText = item.timeString.styled( with: .font(.ows_dynamicTypeCaption1Clamped), @@ -251,8 +271,6 @@ class StoryGroupReplyCell: UITableViewCell { break case .top, .middle: messageLabel.attributedText = messageText - case .reaction: - timestampLabel.text = item.timeString } } diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift index c60030f70e..835ed144e8 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyViewItem.swift @@ -8,6 +8,7 @@ import UIKit class StoryGroupReplyViewItem: Dependencies { let displayableText: DisplayableText? + let reactionEmoji: String? let wasRemotelyDeleted: Bool let receivedAtTimestamp: UInt64 let authorDisplayName: String? @@ -41,6 +42,12 @@ class StoryGroupReplyViewItem: Dependencies { self.authorDisplayName = authorDisplayName self.authorColor = authorColor - self.cellType = .standalone + if let reactionEmoji = message.storyReactionEmoji { + self.cellType = .reaction + self.reactionEmoji = reactionEmoji + } else { + self.cellType = .standalone + self.reactionEmoji = nil + } } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index dd144bc013..ae60940be8 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -6115,6 +6115,9 @@ /* Author text when the local user author's a story reply */ "STORY_REPLY_YOU_AUTHOR" = "You"; +/* Text indicating a story has been reacted to */ +"STORY_REPLY_REACTION" = "Reacted to the story"; + /* placeholder text for replying to a story */ "STORY_REPLY_TEXT_FIELD_PLACEHOLDER" = "Reply"; diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index 0d06331ac6..059940b3ee 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -117,8 +117,9 @@ NS_ASSUME_NONNULL_BEGIN isViewOnceMessage:transcript.isViewOnceMessage changeActionsProtoData:nil additionalRecipients:nil - storyAuthorAddress:nil - storyTimestamp:nil] build]; + storyAuthorAddress:transcript.storyAuthorAddress + storyTimestamp:transcript.storyTimestamp + storyReactionEmoji:nil] build]; SignalServiceAddress *_Nullable localAddress = self.tsAccountManager.localAddress; if (localAddress == nil) { diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h index aea3eb6e6a..05eb0896bd 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @@ -55,6 +55,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) TSPaymentCancellation *paymentCancellation; @property (nonatomic, readonly, nullable) NSNumber *requiredProtocolVersion; @property (nonatomic, readonly) DisappearingMessageToken *disappearingMessageToken; +@property (nonatomic, readonly, nullable) NSNumber *storyTimestamp; +@property (nonatomic, readonly, nullable) SignalServiceAddress *storyAuthorAddress; // If either nonUdRecipientIds or udRecipientIds is nil, // this is either a legacy transcript or it reflects a legacy sync message. diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m index 7778bc1ffd..0851a594b2 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // #import "OWSIncomingSentMessageTranscript.h" @@ -222,6 +222,17 @@ NS_ASSUME_NONNULL_BEGIN _paymentRequest = paymentModels.request; _paymentNotification = paymentModels.notification; _paymentCancellation = paymentModels.cancellation; + + if (_dataMessage.storyContext != nil && _dataMessage.storyContext.hasSentTimestamp + && _dataMessage.storyContext.hasAuthorUuid) { + _storyTimestamp = @(_dataMessage.storyContext.sentTimestamp); + _storyAuthorAddress = [[SignalServiceAddress alloc] initWithUuidString:_dataMessage.storyContext.authorUuid]; + + if (!_storyAuthorAddress.isValid) { + OWSFailDebug(@"Discarding story reply transcript with invalid address %@", _storyAuthorAddress); + return nil; + } + } } if (sentProto.unidentifiedStatus.count > 0) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift index d9f2af1124..447064f0c6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+Builder.swift @@ -41,7 +41,8 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { wasReceivedByUD: Bool = false, isViewOnceMessage: Bool = false, storyAuthorAddress: SignalServiceAddress? = nil, - storyTimestamp: UInt64? = nil) { + storyTimestamp: UInt64? = nil, + storyReactionEmoji: String? = nil) { super.init(thread: thread, timestamp: timestamp, @@ -57,7 +58,8 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { messageSticker: messageSticker, isViewOnceMessage: isViewOnceMessage, storyAuthorAddress: storyAuthorAddress, - storyTimestamp: storyTimestamp) + storyTimestamp: storyTimestamp, + storyReactionEmoji: storyReactionEmoji) self.authorAddress = authorAddress self.sourceDeviceId = sourceDeviceId @@ -101,7 +103,8 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { wasReceivedByUD: Bool, isViewOnceMessage: Bool, storyAuthorAddress: SignalServiceAddress?, - storyTimestamp: NSNumber?) -> TSIncomingMessageBuilder { + storyTimestamp: NSNumber?, + storyReactionEmoji: String?) -> TSIncomingMessageBuilder { return TSIncomingMessageBuilder(thread: thread, timestamp: timestamp, authorAddress: authorAddress, @@ -120,7 +123,8 @@ public class TSIncomingMessageBuilder: TSMessageBuilder { wasReceivedByUD: wasReceivedByUD, isViewOnceMessage: isViewOnceMessage, storyAuthorAddress: storyAuthorAddress, - storyTimestamp: storyTimestamp?.uint64Value) + storyTimestamp: storyTimestamp?.uint64Value, + storyReactionEmoji: storyReactionEmoji) } private var hasBuilt = false diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift b/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift index 29df43ee10..91a289a5ca 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage+Builder.swift @@ -40,6 +40,8 @@ public class TSMessageBuilder: NSObject { @objc public var storyTimestamp: NSNumber? @objc + public var storyReactionEmoji: String? + @objc public var isGroupStoryReply: Bool { storyAuthorAddress != nil && storyTimestamp != nil && thread.isGroupThread } @@ -57,7 +59,8 @@ public class TSMessageBuilder: NSObject { messageSticker: MessageSticker? = nil, isViewOnceMessage: Bool = false, storyAuthorAddress: SignalServiceAddress? = nil, - storyTimestamp: UInt64? = nil) { + storyTimestamp: UInt64? = nil, + storyReactionEmoji: String? = nil) { self.thread = thread if let timestamp = timestamp { @@ -77,6 +80,7 @@ public class TSMessageBuilder: NSObject { self.isViewOnceMessage = isViewOnceMessage self.storyAuthorAddress = storyAuthorAddress self.storyTimestamp = storyTimestamp.map { NSNumber(value: $0) } + self.storyReactionEmoji = storyReactionEmoji } @objc diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index f75a79ef6d..32a47683bb 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -94,6 +94,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _isViewOnceComplete = NO; _storyTimestamp = messageBuilder.storyTimestamp; _storyAuthorUuidString = messageBuilder.storyAuthorAddress.uuidString; + _storyReactionEmoji = messageBuilder.storyReactionEmoji; _isGroupStoryReply = messageBuilder.isGroupStoryReply; #ifdef DEBUG diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift index 43e2fa99f5..a9ff74a6e6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.swift @@ -38,7 +38,8 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { changeActionsProtoData: Data? = nil, additionalRecipients: [SignalServiceAddress]? = nil, storyAuthorAddress: SignalServiceAddress? = nil, - storyTimestamp: UInt64? = nil) { + storyTimestamp: UInt64? = nil, + storyReactionEmoji: String? = nil) { super.init(thread: thread, timestamp: timestamp, @@ -53,7 +54,8 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { messageSticker: messageSticker, isViewOnceMessage: isViewOnceMessage, storyAuthorAddress: storyAuthorAddress, - storyTimestamp: storyTimestamp) + storyTimestamp: storyTimestamp, + storyReactionEmoji: storyReactionEmoji) self.isVoiceMessage = isVoiceMessage self.groupMetaMessage = groupMetaMessage @@ -94,7 +96,8 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { changeActionsProtoData: Data?, additionalRecipients: [SignalServiceAddress]?, storyAuthorAddress: SignalServiceAddress?, - storyTimestamp: NSNumber?) -> TSOutgoingMessageBuilder { + storyTimestamp: NSNumber?, + storyReactionEmoji: String?) -> TSOutgoingMessageBuilder { return TSOutgoingMessageBuilder(thread: thread, timestamp: timestamp, messageBody: messageBody, @@ -112,7 +115,8 @@ public class TSOutgoingMessageBuilder: TSMessageBuilder { changeActionsProtoData: changeActionsProtoData, additionalRecipients: additionalRecipients, storyAuthorAddress: storyAuthorAddress, - storyTimestamp: storyTimestamp?.uint64Value) + storyTimestamp: storyTimestamp?.uint64Value, + storyReactionEmoji: storyReactionEmoji) } private var hasBuilt = false diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 800fc92b10..7ad27f52df 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1605,7 +1605,7 @@ NS_ASSUME_NONNULL_BEGIN return; } OWSReactionProcessingResult result = [OWSReactionManager processIncomingReaction:dataMessage.reaction - threadId:thread.uniqueId + thread:thread reactor:envelope.sourceAddress timestamp:syncMessage.sent.timestamp transaction:transaction]; @@ -1956,7 +1956,7 @@ NS_ASSUME_NONNULL_BEGIN OWSLogInfo(@"Reaction: %@", messageDescription); } OWSReactionProcessingResult result = [OWSReactionManager processIncomingReaction:dataMessage.reaction - threadId:thread.uniqueId + thread:thread reactor:envelope.sourceAddress timestamp:timestamp transaction:transaction]; @@ -2147,7 +2147,8 @@ NS_ASSUME_NONNULL_BEGIN wasReceivedByUD:wasReceivedByUD isViewOnceMessage:isViewOnceMessage storyAuthorAddress:storyAuthorAddress - storyTimestamp:storyTimestamp]; + storyTimestamp:storyTimestamp + storyReactionEmoji:nil]; TSIncomingMessage *message = [incomingMessageBuilder build]; if (!message) { OWSFailDebug(@"Missing incomingMessage."); diff --git a/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift b/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift index 65822f806d..1cf5cb5c18 100644 --- a/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift +++ b/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift @@ -123,7 +123,7 @@ public class ReactionManager: NSObject { @objc public class func processIncomingReaction( _ reaction: SSKProtoDataMessageReaction, - threadId: String, + thread: TSThread, reactor: SignalServiceAddress, timestamp: UInt64, transaction: SDSAnyWriteTransaction @@ -146,50 +146,77 @@ public class ReactionManager: NSObject { return .invalidReaction } - guard let message = InteractionFinder.findMessage( + if let message = InteractionFinder.findMessage( withTimestamp: reaction.timestamp, - threadId: threadId, + threadId: thread.uniqueId, author: messageAuthor, transaction: transaction - ) else { + ) { + guard !message.wasRemotelyDeleted else { + Logger.info("Ignoring reaction for a message that was remotely deleted") + return .invalidReaction + } + + // If this is a reaction removal, we want to remove *any* reaction from this author + // on this message, regardless of the specified emoji. + if reaction.hasRemove, reaction.remove { + message.removeReaction(for: reactor, transaction: transaction) + } else { + let reaction = message.recordReaction( + for: reactor, + emoji: emoji, + sentAtTimestamp: timestamp, + receivedAtTimestamp: NSDate.ows_millisecondTimeStamp(), + transaction: transaction + ) + + // If this is a reaction to a message we sent, notify the user. + if let reaction = reaction, let message = message as? TSOutgoingMessage, !reactor.isLocalAddress { + self.notificationsManager?.notifyUser(forReaction: reaction, + onOutgoingMessage: message, + thread: thread, + transaction: transaction) + } + } + + return .success + } else if let storyMessage = StoryFinder.story( + timestamp: reaction.timestamp, + author: messageAuthor, + transaction: transaction + ) { + // Reaction to stories show up as normal messages, they + // are not associated with standard interactions. As such + // we need to insert an incoming/outgoing message as appropriate. + + func populateStoryContext(on builder: TSMessageBuilder) { + builder.timestamp = timestamp + builder.storyReactionEmoji = reaction.emoji + builder.storyTimestamp = NSNumber(value: storyMessage.timestamp) + builder.storyAuthorAddress = storyMessage.authorAddress + } + + let message: TSMessage + + if reactor.isLocalAddress { + let builder = TSOutgoingMessageBuilder(thread: thread) + populateStoryContext(on: builder) + message = builder.build() + } else { + let builder = TSIncomingMessageBuilder(thread: thread) + builder.authorAddress = reactor + populateStoryContext(on: builder) + message = builder.build() + } + + message.anyInsert(transaction: transaction) + + return .success + } else { // This is potentially normal. For example, we could've deleted the message locally. Logger.info("Received reaction for a message that doesn't exist \(timestamp)") return .associatedMessageMissing } - - guard !message.wasRemotelyDeleted else { - Logger.info("Ignoring reaction for a message that was remotely deleted") - return .invalidReaction - } - - // If this is a reaction removal, we want to remove *any* reaction from this author - // on this message, regardless of the specified emoji. - if reaction.hasRemove, reaction.remove { - message.removeReaction(for: reactor, transaction: transaction) - } else { - let reaction = message.recordReaction( - for: reactor, - emoji: emoji, - sentAtTimestamp: timestamp, - receivedAtTimestamp: NSDate.ows_millisecondTimeStamp(), - transaction: transaction - ) - - // If this is a reaction to a message we sent, notify the user. - if let reaction = reaction, let message = message as? TSOutgoingMessage, !reactor.isLocalAddress { - guard let thread = TSThread.anyFetch(uniqueId: threadId, transaction: transaction) else { - owsFailDebug("Failed to lookup thread for reaction notification.") - return .success - } - - self.notificationsManager?.notifyUser(forReaction: reaction, - onOutgoingMessage: message, - thread: thread, - transaction: transaction) - } - } - - return .success } @objc diff --git a/SignalServiceKit/src/TestUtils/Factories.swift b/SignalServiceKit/src/TestUtils/Factories.swift index b7a7a17c99..6e541da6dd 100644 --- a/SignalServiceKit/src/TestUtils/Factories.swift +++ b/SignalServiceKit/src/TestUtils/Factories.swift @@ -141,7 +141,8 @@ public class OutgoingMessageFactory: NSObject, Factory { changeActionsProtoData: changeActionsProtoDataBuilder(), additionalRecipients: additionalRecipientsBuilder(), storyAuthorAddress: storyAuthorAddressBuilder(), - storyTimestamp: storyTimestampBuilder()).build() + storyTimestamp: storyTimestampBuilder(), + storyReactionEmoji: storyReactionEmojiBuilder()).build() } @objc @@ -246,6 +247,11 @@ public class OutgoingMessageFactory: NSObject, Factory { return nil } + @objc + public var storyReactionEmojiBuilder: () -> String? = { + return nil + } + // MARK: Delivery Receipts @objc @@ -302,7 +308,8 @@ public class IncomingMessageFactory: NSObject, Factory { wasReceivedByUD: wasReceivedByUDBuilder(), isViewOnceMessage: isViewOnceMessageBuilder(), storyAuthorAddress: storyAuthorAddressBuilder(), - storyTimestamp: storyTimestampBuilder()) + storyTimestamp: storyTimestampBuilder(), + storyReactionEmoji: storyReactionEmojiBuilder()) let item = builder.build() item.anyInsert(transaction: transaction) return item @@ -415,6 +422,11 @@ public class IncomingMessageFactory: NSObject, Factory { public var storyTimestampBuilder: () -> NSNumber? = { nil } + + @objc + public var storyReactionEmojiBuilder: () -> String? = { + return nil + } } @objc From 729864b638eacc5fdd62d87a4f52e6fa47728e22 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 16:07:49 -0700 Subject: [PATCH 16/20] Support sending story reactions --- .../StoryGroupReplyLoader.swift | 3 +- .../StoryGroupReplySheet.swift | 157 +++++++++++++----- .../MessageReactionPicker.swift | 12 +- .../Messages/Interactions/TSOutgoingMessage.m | 19 +++ 4 files changed, 147 insertions(+), 44 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift index 55ac3f1a91..e1157cee0b 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyLoader.swift @@ -254,7 +254,8 @@ class StoryGroupReplyLoader: Dependencies { if let previousItem = previousItem { if previousItem.authorAddress == authorAddress && previousItem.timeString == replyItem.timeString - && replyItem.cellType != .reaction { + && replyItem.cellType != .reaction + && previousItem.cellType != .reaction { switch previousItem.cellType { case .standalone: previousItem.cellType = .top diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift index c615fcb057..e36b04408f 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplySheet.swift @@ -23,6 +23,9 @@ class StoryGroupReplySheet: InteractiveSheetViewController { private let storyMessage: StoryMessage private let thread: TSThread? + fileprivate var reactionPickerBackdrop: UIView? + fileprivate var reactionPicker: MessageReactionPicker? + var dismissHandler: (() -> Void)? init(storyMessage: StoryMessage) { @@ -101,6 +104,57 @@ class StoryGroupReplySheet: InteractiveSheetViewController { super.viewDidAppear(animated) replyLoader?.scrollToBottomOfLoadWindow(animated: true) } + + fileprivate func tryToSendMessage(_ message: TSOutgoingMessage) { + guard let thread = thread else { + return owsFailDebug("Unexpectedly missing thread") + } + + guard !blockingManager.isThreadBlocked(thread) else { + BlockListUIUtils.showUnblockThreadActionSheet(thread, from: self) { [weak self] isBlocked in + guard !isBlocked else { return } + self?.tryToSendMessage(message) + } + return + } + + guard !SafetyNumberConfirmationSheet.presentIfNecessary( + addresses: thread.recipientAddresses, + confirmationText: SafetyNumberStrings.confirmSendButton, + completion: { [weak self] didConfirmIdentity in + guard didConfirmIdentity else { return } + self?.tryToSendMessage(message) + } + ) else { return } + + ThreadUtil.enqueueSendAsyncWrite { [weak self] transaction in + ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequest(thread: thread, setDefaultTimerIfNecessary: false, transaction: transaction) + + message.anyInsert(transaction: transaction) + + Self.messageSenderJobQueue.add(message: message.asPreparer, transaction: transaction) + + transaction.addAsyncCompletionOnMain { + self?.replyLoader?.reload() + self?.inputToolbar.messageBody = nil + } + } + } + + func tryToSendReaction(_ reaction: String) { + owsAssertDebug(reaction.isSingleEmoji) + + guard let thread = thread else { + return owsFailDebug("Unexpectedly missing thread") + } + + let builder = TSOutgoingMessageBuilder(thread: thread) + builder.storyReactionEmoji = reaction + builder.storyTimestamp = NSNumber(value: storyMessage.timestamp) + builder.storyAuthorAddress = storyMessage.authorAddress + + tryToSendMessage(builder.build()) + } } extension StoryGroupReplySheet: UIScrollViewDelegate { @@ -207,7 +261,7 @@ extension StoryGroupReplySheet: InputAccessoryViewPlaceholderDelegate { extension StoryGroupReplySheet: StoryReplyInputToolbarDelegate { func storyReplyInputToolbarDidTapReact(_ storyReplyInputToolbar: StoryReplyInputToolbar) { - + presentReactionPicker() } func storyReplyInputToolbarDidTapSend(_ storyReplyInputToolbar: StoryReplyInputToolbar) { @@ -215,52 +269,17 @@ extension StoryGroupReplySheet: StoryReplyInputToolbarDelegate { return owsFailDebug("Unexpectedly missing message body") } - tryToSendTextMessage(messageBody) - } - - func tryToSendTextMessage(_ messageBody: MessageBody) { - owsAssertDebug(!messageBody.text.isEmpty) - guard let thread = thread else { return owsFailDebug("Unexpectedly missing thread") } - guard !blockingManager.isThreadBlocked(thread) else { - BlockListUIUtils.showUnblockThreadActionSheet(thread, from: self) { [weak self] isBlocked in - guard !isBlocked else { return } - self?.tryToSendTextMessage(messageBody) - } - return - } - - guard !SafetyNumberConfirmationSheet.presentIfNecessary( - addresses: thread.recipientAddresses, - confirmationText: SafetyNumberStrings.confirmSendButton, - completion: { [weak self] didConfirmIdentity in - guard didConfirmIdentity else { return } - self?.tryToSendTextMessage(messageBody) - } - ) else { return } - let builder = TSOutgoingMessageBuilder(thread: thread) builder.messageBody = messageBody.text builder.bodyRanges = messageBody.ranges builder.storyTimestamp = NSNumber(value: storyMessage.timestamp) builder.storyAuthorAddress = storyMessage.authorAddress - let message = builder.build() - ThreadUtil.enqueueSendAsyncWrite { [weak self] transaction in - ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequest(thread: thread, setDefaultTimerIfNecessary: false, transaction: transaction) - - message.anyInsert(transaction: transaction) - - Self.messageSenderJobQueue.add(message: message.asPreparer, transaction: transaction) - - transaction.addAsyncCompletionOnMain { - self?.replyLoader?.reload() - self?.inputToolbar.messageBody = nil - } - } + tryToSendMessage(builder.build()) } func storyReplyInputToolbarDidBeginEditing(_ storyReplyInputToolbar: StoryReplyInputToolbar) { @@ -275,3 +294,67 @@ extension StoryGroupReplySheet: StoryReplyInputToolbarDelegate { return thread?.recipientAddresses ?? [] } } + +extension StoryGroupReplySheet: MessageReactionPickerDelegate { + func didSelectReaction(reaction: String, isRemoving: Bool, inPosition position: Int) { + dismissReactionPicker() + + tryToSendReaction(reaction) + } + + func didSelectAnyEmoji() { + dismissReactionPicker() + + let sheet = EmojiPickerSheet { [weak self] selectedEmoji in + guard let selectedEmoji = selectedEmoji else { return } + self?.tryToSendReaction(selectedEmoji.rawValue) + } + present(sheet, animated: true) + } + + @objc + func didTapReactionPickerBackdrop() { + dismissReactionPicker() + } + + func presentReactionPicker() { + guard self.reactionPicker == nil else { return } + + let backdrop = UIView() + backdrop.backgroundColor = .ows_blackAlpha40 + view.addSubview(backdrop) + backdrop.autoPinEdgesToSuperviewEdges() + backdrop.alpha = 0 + self.reactionPickerBackdrop = backdrop + + let backdropTapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapReactionPickerBackdrop)) + backdrop.addGestureRecognizer(backdropTapGesture) + backdrop.isUserInteractionEnabled = true + + let reactionPicker = MessageReactionPicker(selectedEmoji: nil, delegate: self, forceDarkTheme: true) + + view.addSubview(reactionPicker) + reactionPicker.autoPinEdge(.bottom, to: .top, of: inputToolbar, withOffset: -15) + reactionPicker.autoPinEdge(toSuperviewEdge: .trailing, withInset: 12) + + reactionPicker.playPresentationAnimation(duration: 0.2) + + UIView.animate(withDuration: 0.2) { backdrop.alpha = 1 } + + self.reactionPicker = reactionPicker + } + + func dismissReactionPicker() { + UIView.animate(withDuration: 0.2) { + self.reactionPickerBackdrop?.alpha = 0 + } completion: { _ in + self.reactionPickerBackdrop?.removeFromSuperview() + self.reactionPickerBackdrop = nil + } + + reactionPicker?.playDismissalAnimation(duration: 0.2) { + self.reactionPicker?.removeFromSuperview() + self.reactionPicker = nil + } + } +} diff --git a/Signal/src/ViewControllers/MessageReactionPicker.swift b/Signal/src/ViewControllers/MessageReactionPicker.swift index 76bdef1795..91f1ddd95e 100644 --- a/Signal/src/ViewControllers/MessageReactionPicker.swift +++ b/Signal/src/ViewControllers/MessageReactionPicker.swift @@ -24,7 +24,7 @@ class MessageReactionPicker: UIStackView { private var buttonForEmoji = [(emoji: String, button: OWSFlatButton)]() private var selectedEmoji: EmojiWithSkinTones? private var backgroundView: UIView? - init(selectedEmoji: String?, delegate: MessageReactionPickerDelegate?, configureMode: Bool = false) { + init(selectedEmoji: String?, delegate: MessageReactionPickerDelegate?, configureMode: Bool = false, forceDarkTheme: Bool = false) { self.configureMode = configureMode if let selectedEmoji = selectedEmoji { @@ -38,7 +38,7 @@ class MessageReactionPicker: UIStackView { super.init(frame: .zero) backgroundView = addBackgroundView( - withBackgroundColor: Theme.actionSheetBackgroundColor, + withBackgroundColor: forceDarkTheme ? .ows_gray75 : Theme.actionSheetBackgroundColor, cornerRadius: pickerDiameter / 2 ) backgroundView?.layer.shadowColor = UIColor.ows_black.cgColor @@ -47,7 +47,7 @@ class MessageReactionPicker: UIStackView { backgroundView?.layer.shadowOffset = .zero let shadowView = UIView() - shadowView.backgroundColor = Theme.actionSheetBackgroundColor + shadowView.backgroundColor = forceDarkTheme ? .ows_gray75 : Theme.actionSheetBackgroundColor shadowView.layer.cornerRadius = pickerDiameter / 2 shadowView.layer.shadowColor = UIColor.ows_black.cgColor shadowView.layer.shadowRadius = 12 @@ -96,7 +96,7 @@ class MessageReactionPicker: UIStackView { for (index, emoji) in emojiSet.enumerated() { let button = OWSFlatButton() button.autoSetDimensions(to: CGSize(square: reactionHeight)) - button.setTitle(title: emoji.rawValue, font: .systemFont(ofSize: reactionFontSize), titleColor: Theme.primaryTextColor) + button.setTitle(title: emoji.rawValue, font: .systemFont(ofSize: reactionFontSize), titleColor: forceDarkTheme ? Theme.darkThemePrimaryColor : Theme.primaryTextColor) button.setPressedBlock { [weak self] in // current title of button may have changed in the meantime if let currentEmoji = button.button.title(for: .normal) { @@ -109,7 +109,7 @@ class MessageReactionPicker: UIStackView { // Add a circle behind the currently selected emoji if self.selectedEmoji == emoji { let selectedBackgroundView = UIView() - selectedBackgroundView.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray60 : .ows_gray05 + selectedBackgroundView.backgroundColor = Theme.isDarkThemeEnabled || forceDarkTheme ? .ows_gray60 : .ows_gray05 selectedBackgroundView.clipsToBounds = true selectedBackgroundView.layer.cornerRadius = selectedBackgroundHeight / 2 backgroundView?.addSubview(selectedBackgroundView) @@ -122,7 +122,7 @@ class MessageReactionPicker: UIStackView { if addAnyButton { let button = OWSFlatButton() button.autoSetDimensions(to: CGSize(square: reactionHeight)) - button.setImage(Theme.isDarkThemeEnabled ? #imageLiteral(resourceName: "any-emoji-32-dark") : #imageLiteral(resourceName: "any-emoji-32-light")) + button.setImage(Theme.isDarkThemeEnabled || forceDarkTheme ? #imageLiteral(resourceName: "any-emoji-32-dark") : #imageLiteral(resourceName: "any-emoji-32-light")) button.setPressedBlock { [weak self] in self?.delegate?.didSelectAnyEmoji() } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 3f662e28ab..6f7992abb0 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -1093,6 +1093,25 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1; // Story Context if (self.storyTimestamp && self.storyAuthorUuidString) { + if (self.storyReactionEmoji) { + SSKProtoDataMessageReactionBuilder *reactionBuilder = + [SSKProtoDataMessageReaction builderWithEmoji:self.storyReactionEmoji + timestamp:self.storyTimestamp.longLongValue]; + [reactionBuilder setAuthorUuid:self.storyAuthorUuidString]; + + NSError *error; + SSKProtoDataMessageReaction *_Nullable reaction = [reactionBuilder buildAndReturnError:&error]; + if (error || !reaction) { + OWSFailDebug(@"Could not build story reaction protobuf: %@.", error); + } else { + [builder setReaction:reaction]; + + if (requiredProtocolVersion < SSKProtoDataMessageProtocolVersionReactions) { + requiredProtocolVersion = SSKProtoDataMessageProtocolVersionReactions; + } + } + } + SSKProtoDataMessageStoryContextBuilder *storyContextBuilder = [SSKProtoDataMessageStoryContext builder]; [storyContextBuilder setAuthorUuid:self.storyAuthorUuidString]; [storyContextBuilder setSentTimestamp:self.storyTimestamp.longLongValue]; From 0b662ed6e2138db354514f754c2765a57278a15f Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Mon, 28 Mar 2022 16:52:42 -0700 Subject: [PATCH 17/20] Fix lint issues --- .../Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m | 3 ++- SignalServiceKit/src/Messages/OWSMessageManager.m | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m index 0851a594b2..6d5209c007 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m @@ -226,7 +226,8 @@ NS_ASSUME_NONNULL_BEGIN if (_dataMessage.storyContext != nil && _dataMessage.storyContext.hasSentTimestamp && _dataMessage.storyContext.hasAuthorUuid) { _storyTimestamp = @(_dataMessage.storyContext.sentTimestamp); - _storyAuthorAddress = [[SignalServiceAddress alloc] initWithUuidString:_dataMessage.storyContext.authorUuid]; + _storyAuthorAddress = + [[SignalServiceAddress alloc] initWithUuidString:_dataMessage.storyContext.authorUuid]; if (!_storyAuthorAddress.isValid) { OWSFailDebug(@"Discarding story reply transcript with invalid address %@", _storyAuthorAddress); diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 7ad27f52df..96d896ef13 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1605,7 +1605,7 @@ NS_ASSUME_NONNULL_BEGIN return; } OWSReactionProcessingResult result = [OWSReactionManager processIncomingReaction:dataMessage.reaction - thread:thread + thread:thread reactor:envelope.sourceAddress timestamp:syncMessage.sent.timestamp transaction:transaction]; @@ -1956,7 +1956,7 @@ NS_ASSUME_NONNULL_BEGIN OWSLogInfo(@"Reaction: %@", messageDescription); } OWSReactionProcessingResult result = [OWSReactionManager processIncomingReaction:dataMessage.reaction - thread:thread + thread:thread reactor:envelope.sourceAddress timestamp:timestamp transaction:transaction]; From df4eed775b0bc8e852150b26e4731cd189145a6f Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Tue, 29 Mar 2022 17:42:46 -0700 Subject: [PATCH 18/20] Make sure `storyReactionEmoji` is cleared when story reactions are remotely deleted --- SignalServiceKit/src/Messages/Interactions/TSMessage.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 32a47683bb..d0a14f14b4 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -58,6 +58,8 @@ static const NSUInteger OWSMessageSchemaVersion = 4; @property (nonatomic) BOOL isViewOnceComplete; @property (nonatomic) BOOL wasRemotelyDeleted; +@property (nonatomic, nullable) NSString *storyReactionEmoji; + // This property is only intended to be used by GRDB queries. @property (nonatomic, readonly) BOOL storedShouldStartExpireTimer; @@ -890,6 +892,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; message.linkPreview = nil; message.messageSticker = nil; message.attachmentIds = @[]; + message.storyReactionEmoji = nil; OWSAssertDebug(!message.hasRenderableContent); messageUpdateBlock(message); From c4b9f18a1a5f359a45f97dc6d2cb36881512055f Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Tue, 29 Mar 2022 21:01:03 -0700 Subject: [PATCH 19/20] Properly store serverTimestamp on incoming story reactions --- SignalServiceKit/src/Messages/OWSMessageManager.m | 2 ++ SignalServiceKit/src/Messages/Reactions/ReactionManager.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 96d896ef13..aba8b94d4d 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1608,6 +1608,7 @@ NS_ASSUME_NONNULL_BEGIN thread:thread reactor:envelope.sourceAddress timestamp:syncMessage.sent.timestamp + serverTimestamp:envelope.serverTimestamp transaction:transaction]; switch (result) { case OWSReactionProcessingResultSuccess: @@ -1959,6 +1960,7 @@ NS_ASSUME_NONNULL_BEGIN thread:thread reactor:envelope.sourceAddress timestamp:timestamp + serverTimestamp:envelope.serverTimestamp transaction:transaction]; switch (result) { diff --git a/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift b/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift index 1cf5cb5c18..d9af0281ae 100644 --- a/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift +++ b/SignalServiceKit/src/Messages/Reactions/ReactionManager.swift @@ -126,6 +126,7 @@ public class ReactionManager: NSObject { thread: TSThread, reactor: SignalServiceAddress, timestamp: UInt64, + serverTimestamp: UInt64, transaction: SDSAnyWriteTransaction ) -> ReactionProcessingResult { guard let emoji = reaction.emoji.strippedOrNil else { @@ -205,6 +206,7 @@ public class ReactionManager: NSObject { } else { let builder = TSIncomingMessageBuilder(thread: thread) builder.authorAddress = reactor + builder.serverTimestamp = NSNumber(value: serverTimestamp) populateStoryContext(on: builder) message = builder.build() } From f4c2e743fb286e1fa71311bbd132e22288e7bcb5 Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Wed, 30 Mar 2022 00:17:30 -0700 Subject: [PATCH 20/20] Cleanup StoryGroupReplyCell --- .../StoryGroupReplyCell.swift | 361 +++++++++--------- 1 file changed, 185 insertions(+), 176 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift index 9082623b55..a5f3839469 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Group Reply Sheet/StoryGroupReplyCell.swift @@ -38,6 +38,69 @@ class StoryGroupReplyCell: UITableViewCell { case bottom case middle case reaction + + var hasTimestamp: Bool { + switch self { + case .standalone, .bottom, .reaction: return true + case .top, .middle: return false + } + } + + var hasAuthor: Bool { + switch self { + case .standalone, .top, .reaction: return true + case .middle, .bottom: return false + } + } + + var hasAvatar: Bool { + switch self { + case .standalone, .bottom, .reaction: return true + case .top, .middle: return false + } + } + + var hasBubble: Bool { + switch self { + case .reaction: return false + case .standalone, .top, .middle, .bottom: return true + } + } + + var hasReaction: Bool { + switch self { + case .reaction: return true + case .standalone, .top, .middle, .bottom: return false + } + } + + var insets: UIEdgeInsets { + switch self { + case .standalone, .reaction: return UIEdgeInsets(hMargin: 16, vMargin: 6) + case .top: return UIEdgeInsets(top: 6, leading: 16, bottom: 1, trailing: 16) + case .middle: return UIEdgeInsets(top: 1, leading: 16, bottom: 1, trailing: 16) + case .bottom: return UIEdgeInsets(top: 1, leading: 16, bottom: 6, trailing: 16) + } + } + + var verticalAlignment: UIStackView.Alignment { + switch self { + case .reaction: return .center + case .standalone, .top, .middle, .bottom: return .bottom + } + } + + var sharpCorners: UIRectCorner { + switch self { + case .standalone, .reaction: return [] + case .top: return CurrentAppContext().isRTL ? .bottomRight : .bottomLeft + case .middle: return CurrentAppContext().isRTL ? [.bottomRight, .topRight] : [.bottomLeft, .topLeft] + case .bottom: return CurrentAppContext().isRTL ? .topRight : .topLeft + } + } + + var cornerRadius: CGFloat { 18 } + var sharpCornerRadius: CGFloat { 4 } } let cellType: CellType @@ -60,79 +123,48 @@ class StoryGroupReplyCell: UITableViewCell { let hStack = UIStackView() hStack.axis = .horizontal - hStack.alignment = .bottom + hStack.alignment = cellType.verticalAlignment + hStack.isLayoutMarginsRelativeArrangement = true + hStack.layoutMargins = cellType.insets - switch cellType { - case .standalone: - hStack.layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 6) + if cellType.hasAvatar { avatarView.autoSetDimensions(to: CGSize(square: 28)) hStack.addArrangedSubview(avatarView) - hStack.addArrangedSubview(.spacer(withWidth: 8)) - hStack.addArrangedSubview(bubbleView) - hStack.addArrangedSubview(.hStretchingSpacer()) - - bubbleView.layer.cornerRadius = 18 - bubbleView.clipsToBounds = true - - bubbleView.addSubview(vStack) - vStack.autoPinEdgesToSuperviewMargins() - - vStack.addArrangedSubview(authorNameLabel) - vStack.addArrangedSubview(messageLabel) - case .top: - hStack.layoutMargins = UIEdgeInsets(top: 6, leading: 16, bottom: 1, trailing: 16) - hStack.addArrangedSubview(.spacer(withWidth: 36)) - hStack.addArrangedSubview(bubbleView) - hStack.addArrangedSubview(.hStretchingSpacer()) - - bubbleView.layer.mask = bubbleCornerMaskLayer - - bubbleView.addSubview(vStack) - vStack.autoPinEdgesToSuperviewMargins() - - vStack.addArrangedSubview(authorNameLabel) - vStack.addArrangedSubview(messageLabel) - case .bottom: - hStack.layoutMargins = UIEdgeInsets(top: 1, leading: 16, bottom: 6, trailing: 16) - hStack.addArrangedSubview(avatarView) - hStack.addArrangedSubview(.spacer(withWidth: 8)) - hStack.addArrangedSubview(bubbleView) - hStack.addArrangedSubview(.hStretchingSpacer()) - - bubbleView.layer.mask = bubbleCornerMaskLayer - - bubbleView.addSubview(vStack) - vStack.autoPinEdgesToSuperviewMargins() - - vStack.addArrangedSubview(authorNameLabel) - vStack.addArrangedSubview(messageLabel) - case .middle: - hStack.layoutMargins = UIEdgeInsets(top: 1, leading: 16, bottom: 1, trailing: 16) - hStack.addArrangedSubview(.spacer(withWidth: 36)) - hStack.addArrangedSubview(bubbleView) - hStack.addArrangedSubview(.hStretchingSpacer()) - - bubbleView.layer.mask = bubbleCornerMaskLayer - - bubbleView.addSubview(vStack) - vStack.autoPinEdgesToSuperviewMargins() - - vStack.addArrangedSubview(messageLabel) - case .reaction: - hStack.alignment = .center - hStack.layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 6) - avatarView.autoSetDimensions(to: CGSize(square: 28)) - hStack.addArrangedSubview(avatarView) - hStack.addArrangedSubview(.spacer(withWidth: 20)) - hStack.addArrangedSubview(vStack) - hStack.addArrangedSubview(.hStretchingSpacer()) - hStack.addArrangedSubview(reactionLabel) - - vStack.addArrangedSubview(authorNameLabel) - vStack.addArrangedSubview(messageLabel) + } else { + hStack.addArrangedSubview(.spacer(withWidth: 28)) } + hStack.addArrangedSubview(.spacer(withWidth: 8)) + + if cellType.hasBubble { + hStack.addArrangedSubview(bubbleView) + bubbleView.addSubview(vStack) + vStack.autoPinEdgesToSuperviewMargins() + + if cellType.sharpCorners.isEmpty { + bubbleView.layer.cornerRadius = cellType.cornerRadius + bubbleView.clipsToBounds = true + } else { + bubbleView.layer.mask = bubbleCornerMaskLayer + } + } else { + hStack.addArrangedSubview(.spacer(withWidth: 12)) + hStack.addArrangedSubview(vStack) + } + + hStack.addArrangedSubview(.hStretchingSpacer()) + + if cellType.hasReaction { + hStack.addArrangedSubview(reactionLabel) + } + + if cellType.hasAuthor { + vStack.addArrangedSubview(authorNameLabel) + } + + vStack.addArrangedSubview(messageLabel) + contentView.addSubview(hStack) hStack.autoPinEdgesToSuperviewEdges() } @@ -142,29 +174,19 @@ class StoryGroupReplyCell: UITableViewCell { } func configure(with item: StoryGroupReplyViewItem) { - switch cellType { - case .standalone: + if cellType.hasAuthor { authorNameLabel.textColor = item.authorColor authorNameLabel.text = item.authorDisplayName + } + + if cellType.hasAvatar { avatarView.updateWithSneakyTransactionIfNecessary { config in config.dataSource = .address(item.authorAddress) } - case .top: - authorNameLabel.textColor = item.authorColor - authorNameLabel.text = item.authorDisplayName - case .bottom: - avatarView.updateWithSneakyTransactionIfNecessary { config in - config.dataSource = .address(item.authorAddress) - } - case .middle: - break - case .reaction: - authorNameLabel.textColor = item.authorColor - authorNameLabel.text = item.authorDisplayName + } + + if cellType.hasReaction { reactionLabel.text = item.reactionEmoji - avatarView.updateWithSneakyTransactionIfNecessary { config in - config.dataSource = .address(item.authorAddress) - } } configureTextAndTimestamp(for: item) @@ -194,83 +216,82 @@ class StoryGroupReplyCell: UITableViewCell { } }() else { return } - switch cellType { - case .standalone, .bottom, .reaction: - // Append timestamp to attributed text - let timestampText = item.timeString.styled( - with: .font(.ows_dynamicTypeCaption1Clamped), - .color(.ows_gray25) - ) - - let maxMessageWidth = min(512, CurrentAppContext().frame.width) - 92 - let timestampSpacer: CGFloat = 6 - - let messageMeasurement = measure(messageText, maxWidth: maxMessageWidth) - let timestampMeasurement = measure(timestampText, maxWidth: maxMessageWidth) - - let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageMeasurement.lastLineRect.width - - let textDirectionMatchesAppDirection: Bool - switch item.displayableText?.displayTextNaturalAlignment ?? .natural { - case .left: - textDirectionMatchesAppDirection = !CurrentAppContext().isRTL - case .right: - textDirectionMatchesAppDirection = CurrentAppContext().isRTL - case .natural: - textDirectionMatchesAppDirection = true - default: - owsFailDebug("Unexpected text alignment") - textDirectionMatchesAppDirection = true - } - - let hasSpacedForTimestampOnLastMessageLine = lastLineFreeSpace >= timestampMeasurement.rect.width - let shouldRenderTimestampOnLastMessageLine = hasSpacedForTimestampOnLastMessageLine && textDirectionMatchesAppDirection - - if shouldRenderTimestampOnLastMessageLine { - var possibleMessageBubbleWidths = [ - messageMeasurement.rect.width, - messageMeasurement.lastLineRect.width + timestampSpacer + timestampMeasurement.rect.width - ] - if cellType == .standalone { - contentView.layoutIfNeeded() - possibleMessageBubbleWidths.append(authorNameLabel.width) - } - - let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! - - messageLabel.attributedText = .composed(of: [ - messageText, - "\n", - timestampText.styled( - with: .paragraphSpacingBefore(-timestampMeasurement.rect.height), - .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) - ) - ]) - } else { - var possibleMessageBubbleWidths = [ - messageMeasurement.rect.width, - timestampMeasurement.rect.width - ] - if cellType == .standalone { - contentView.layoutIfNeeded() - possibleMessageBubbleWidths.append(authorNameLabel.width) - } - - let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! - - messageLabel.attributedText = .composed(of: [ - messageText, - "\n", - timestampText.styled( - with: textDirectionMatchesAppDirection - ? .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) - : .alignment(.trailing) - ) - ]) - } - break - case .top, .middle: + guard cellType.hasTimestamp else { messageLabel.attributedText = messageText + return + } + + // Append timestamp to attributed text + let timestampText = item.timeString.styled( + with: .font(.ows_dynamicTypeCaption1Clamped), + .color(.ows_gray25) + ) + + let maxMessageWidth = min(512, CurrentAppContext().frame.width) - 92 + let timestampSpacer: CGFloat = 6 + + let messageMeasurement = measure(messageText, maxWidth: maxMessageWidth) + let timestampMeasurement = measure(timestampText, maxWidth: maxMessageWidth) + + let lastLineFreeSpace = maxMessageWidth - timestampSpacer - messageMeasurement.lastLineRect.width + + let textDirectionMatchesAppDirection: Bool + switch item.displayableText?.displayTextNaturalAlignment ?? .natural { + case .left: + textDirectionMatchesAppDirection = !CurrentAppContext().isRTL + case .right: + textDirectionMatchesAppDirection = CurrentAppContext().isRTL + case .natural: + textDirectionMatchesAppDirection = true + default: + owsFailDebug("Unexpected text alignment") + textDirectionMatchesAppDirection = true + } + + let hasSpacedForTimestampOnLastMessageLine = lastLineFreeSpace >= timestampMeasurement.rect.width + let shouldRenderTimestampOnLastMessageLine = hasSpacedForTimestampOnLastMessageLine && textDirectionMatchesAppDirection + + if shouldRenderTimestampOnLastMessageLine { + var possibleMessageBubbleWidths = [ + messageMeasurement.rect.width, + messageMeasurement.lastLineRect.width + timestampSpacer + timestampMeasurement.rect.width + ] + if cellType.hasAuthor { + contentView.layoutIfNeeded() + possibleMessageBubbleWidths.append(authorNameLabel.width) + } + + let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! + + messageLabel.attributedText = .composed(of: [ + messageText, + "\n", + timestampText.styled( + with: .paragraphSpacingBefore(-timestampMeasurement.rect.height), + .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) + ) + ]) + } else { + var possibleMessageBubbleWidths = [ + messageMeasurement.rect.width, + timestampMeasurement.rect.width + ] + if cellType.hasAuthor { + contentView.layoutIfNeeded() + possibleMessageBubbleWidths.append(authorNameLabel.width) + } + + let finalMessageLabelWidth = possibleMessageBubbleWidths.max()! + + messageLabel.attributedText = .composed(of: [ + messageText, + "\n", + timestampText.styled( + with: textDirectionMatchesAppDirection + ? .firstLineHeadIndent(finalMessageLabelWidth - timestampMeasurement.rect.width) + : .alignment(.trailing) + ) + ]) } } @@ -303,26 +324,14 @@ class StoryGroupReplyCell: UITableViewCell { override func layoutSubviews() { super.layoutSubviews() - let sharpCorners: UIRectCorner - - switch cellType { - case .standalone, .reaction: - // No special corner rounding to apply - return - case .middle: - sharpCorners = CurrentAppContext().isRTL ? [.bottomRight, .topRight] : [.bottomLeft, .topLeft] - case .top: - sharpCorners = CurrentAppContext().isRTL ? .bottomRight : .bottomLeft - case .bottom: - sharpCorners = CurrentAppContext().isRTL ? .topRight : .topLeft - } + guard !cellType.sharpCorners.isEmpty else { return } bubbleView.layoutIfNeeded() bubbleCornerMaskLayer.path = UIBezierPath.roundedRect( bubbleView.bounds, - sharpCorners: sharpCorners, - sharpCornerRadius: 4, - wideCornerRadius: 18 + sharpCorners: cellType.sharpCorners, + sharpCornerRadius: cellType.sharpCornerRadius, + wideCornerRadius: cellType.cornerRadius ).cgPath } }