mirror of
https://github.com/signalapp/Signal-iOS.git
synced 2025-12-05 01:10:41 +00:00
Promote chats with drafts to the top of the Chat List
This commit is contained in:
@@ -157,6 +157,8 @@
|
||||
"ThreadRecord.hasDismissedOffers": 11,
|
||||
"ThreadRecord.isArchivedObsolete": 3,
|
||||
"ThreadRecord.isMarkedUnreadObsolete": 12,
|
||||
"ThreadRecord.lastDraftInteractionRowId": 24,
|
||||
"ThreadRecord.lastDraftUpdateTimestamp": 25,
|
||||
"ThreadRecord.lastInteractionRowId": 4,
|
||||
"ThreadRecord.lastReceivedStoryTimestamp": 24,
|
||||
"ThreadRecord.lastSentStoryTimestamp": 19,
|
||||
@@ -171,4 +173,4 @@
|
||||
"ThreadRecord.name": 20,
|
||||
"ThreadRecord.shouldThreadBeVisible": 7,
|
||||
"ThreadRecord.storyViewMode": 22
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
041A5F072E05B3F900FAED05 /* BackupEnablementMegaphone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041A5F062E05B3F000FAED05 /* BackupEnablementMegaphone.swift */; };
|
||||
041C24ED2DF782AF0065B685 /* OutgoingGroupUpdateMessageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9BC9C6428B7C00A0077D442 /* OutgoingGroupUpdateMessageTest.swift */; };
|
||||
043CC30E2E1EF79C00D9002E /* ThreadFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043CC30D2E1EF79300D9002E /* ThreadFinderTests.swift */; };
|
||||
047A6DD02E00B5720048EDF4 /* BackupKeyReminderMegaphoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047A6DCF2E00B5640048EDF4 /* BackupKeyReminderMegaphoneTests.swift */; };
|
||||
04AA85BC2E0EFC5C0051C0A7 /* BackupEnablementReminderMegaphoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041A5F082E05D3AC00FAED05 /* BackupEnablementReminderMegaphoneTests.swift */; };
|
||||
04BC94D22E061D8300446C52 /* BackupArchiveAttachmentByteCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BC94D12E061D7500446C52 /* BackupArchiveAttachmentByteCounter.swift */; };
|
||||
@@ -3836,6 +3837,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
041A5F062E05B3F000FAED05 /* BackupEnablementMegaphone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupEnablementMegaphone.swift; sourceTree = "<group>"; };
|
||||
041A5F082E05D3AC00FAED05 /* BackupEnablementReminderMegaphoneTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupEnablementReminderMegaphoneTests.swift; sourceTree = "<group>"; };
|
||||
043CC30D2E1EF79300D9002E /* ThreadFinderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadFinderTests.swift; sourceTree = "<group>"; };
|
||||
047A6DCF2E00B5640048EDF4 /* BackupKeyReminderMegaphoneTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupKeyReminderMegaphoneTests.swift; sourceTree = "<group>"; };
|
||||
04BC94D12E061D7500446C52 /* BackupArchiveAttachmentByteCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupArchiveAttachmentByteCounter.swift; sourceTree = "<group>"; };
|
||||
04E66D3E2DFC81BC0059DBAC /* BackupKeyReminderMegaphone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupKeyReminderMegaphone.swift; sourceTree = "<group>"; };
|
||||
@@ -13373,6 +13375,7 @@
|
||||
F942622E289B1B5500460798 /* SMKTestUtils.swift */,
|
||||
F9426230289B1B5500460798 /* SMKUDAccessKeyTest.swift */,
|
||||
F942621E289B1B5500460798 /* TestProtocolRunnerTest.swift */,
|
||||
043CC30D2E1EF79300D9002E /* ThreadFinderTests.swift */,
|
||||
D9AD1D9428B9955C00B42E6F /* TSInfoMessage+GroupUpdateType+NSAttributedStringTest.swift */,
|
||||
F9426227289B1B5500460798 /* TypingIndicatorMessageTest.swift */,
|
||||
502346742DB017420029DB97 /* ValidatedIncomingEnvelopeTest.swift */,
|
||||
@@ -19011,6 +19014,7 @@
|
||||
663B9CB12C9DF55D0055DC7D /* TaskQueueLoaderTest.swift in Sources */,
|
||||
F9426288289B1B5600460798 /* TestProtocolRunnerTest.swift in Sources */,
|
||||
6600F38B299016BC00B1EDB7 /* TestSchedulerTest.swift in Sources */,
|
||||
043CC30E2E1EF79C00D9002E /* ThreadFinderTests.swift in Sources */,
|
||||
5011D9702A0429B6000FE8E5 /* ThreadMergerTest.swift in Sources */,
|
||||
50C38CAD2A8EB2610030A731 /* TimeGatedBatchTest.swift in Sources */,
|
||||
50D1EEAE2D95D95A00ADF921 /* TimeIntervalTest.swift in Sources */,
|
||||
|
||||
@@ -78,14 +78,26 @@ public class CLVLoader {
|
||||
.pinnedThreadIds(tx: transaction)
|
||||
|
||||
let visibleThreadUniqueIds: [String]
|
||||
if isViewingArchive {
|
||||
visibleThreadUniqueIds = try threadFinder.visibleArchivedThreadIds(transaction: transaction)
|
||||
if FeatureFlags.moveDraftsUpChatList {
|
||||
if isViewingArchive {
|
||||
visibleThreadUniqueIds = try threadFinder.internal_visibleArchivedThreadIds(transaction: transaction)
|
||||
} else {
|
||||
visibleThreadUniqueIds = try threadFinder.internal_visibleInboxThreadIds(
|
||||
filteredBy: viewInfo.inboxFilter,
|
||||
requiredVisibleThreadIds: viewInfo.requiredVisibleThreadIds,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
} else {
|
||||
visibleThreadUniqueIds = try threadFinder.visibleInboxThreadIds(
|
||||
filteredBy: viewInfo.inboxFilter,
|
||||
requiredVisibleThreadIds: viewInfo.requiredVisibleThreadIds,
|
||||
transaction: transaction
|
||||
)
|
||||
if isViewingArchive {
|
||||
visibleThreadUniqueIds = try threadFinder.visibleArchivedThreadIds(transaction: transaction)
|
||||
} else {
|
||||
visibleThreadUniqueIds = try threadFinder.visibleInboxThreadIds(
|
||||
filteredBy: viewInfo.inboxFilter,
|
||||
requiredVisibleThreadIds: viewInfo.requiredVisibleThreadIds,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var pinnedThreadUniqueIdsToRender = Set<String>()
|
||||
|
||||
@@ -53,6 +53,8 @@ public struct ThreadRecord: SDSRecord {
|
||||
public let addresses: Data?
|
||||
public let storyViewMode: UInt
|
||||
public let editTargetTimestamp: UInt64?
|
||||
public let lastDraftInteractionRowId: UInt64
|
||||
public let lastDraftUpdateTimestamp: UInt64
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
|
||||
case id
|
||||
@@ -81,6 +83,8 @@ public struct ThreadRecord: SDSRecord {
|
||||
case addresses
|
||||
case storyViewMode
|
||||
case editTargetTimestamp
|
||||
case lastDraftInteractionRowId
|
||||
case lastDraftUpdateTimestamp
|
||||
}
|
||||
|
||||
public static func columnName(_ column: ThreadRecord.CodingKeys, fullyQualified: Bool = false) -> String {
|
||||
@@ -130,6 +134,8 @@ public extension ThreadRecord {
|
||||
addresses = row[23]
|
||||
storyViewMode = row[24]
|
||||
editTargetTimestamp = row[25]
|
||||
lastDraftInteractionRowId = row[26]
|
||||
lastDraftUpdateTimestamp = row[27]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +172,8 @@ extension TSThread {
|
||||
let editTargetTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.editTargetTimestamp, name: "editTargetTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let isArchivedObsolete: Bool = record.isArchived
|
||||
let isMarkedUnreadObsolete: Bool = record.isMarkedUnread
|
||||
let lastDraftInteractionRowId: UInt64 = record.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = record.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = record.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.lastSentStoryTimestamp, name: "lastSentStoryTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let lastVisibleSortIdObsolete: UInt64 = record.lastVisibleSortId
|
||||
@@ -190,6 +198,8 @@ extension TSThread {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -214,6 +224,8 @@ extension TSThread {
|
||||
let editTargetTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.editTargetTimestamp, name: "editTargetTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let isArchivedObsolete: Bool = record.isArchived
|
||||
let isMarkedUnreadObsolete: Bool = record.isMarkedUnread
|
||||
let lastDraftInteractionRowId: UInt64 = record.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = record.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = record.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.lastSentStoryTimestamp, name: "lastSentStoryTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let lastVisibleSortIdObsolete: UInt64 = record.lastVisibleSortId
|
||||
@@ -237,6 +249,8 @@ extension TSThread {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -259,6 +273,8 @@ extension TSThread {
|
||||
let editTargetTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.editTargetTimestamp, name: "editTargetTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let isArchivedObsolete: Bool = record.isArchived
|
||||
let isMarkedUnreadObsolete: Bool = record.isMarkedUnread
|
||||
let lastDraftInteractionRowId: UInt64 = record.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = record.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = record.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.lastSentStoryTimestamp, name: "lastSentStoryTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let lastVisibleSortIdObsolete: UInt64 = record.lastVisibleSortId
|
||||
@@ -283,6 +299,8 @@ extension TSThread {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -307,6 +325,8 @@ extension TSThread {
|
||||
let editTargetTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.editTargetTimestamp, name: "editTargetTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let isArchivedObsolete: Bool = record.isArchived
|
||||
let isMarkedUnreadObsolete: Bool = record.isMarkedUnread
|
||||
let lastDraftInteractionRowId: UInt64 = record.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = record.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = record.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.lastSentStoryTimestamp, name: "lastSentStoryTimestamp", conversion: { NSNumber(value: $0) })
|
||||
let lastVisibleSortIdObsolete: UInt64 = record.lastVisibleSortId
|
||||
@@ -328,6 +348,8 @@ extension TSThread {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -402,6 +424,8 @@ extension TSThread: DeepCopyable {
|
||||
let editTargetTimestamp: NSNumber? = modelToCopy.editTargetTimestamp
|
||||
let isArchivedObsolete: Bool = modelToCopy.isArchivedObsolete
|
||||
let isMarkedUnreadObsolete: Bool = modelToCopy.isMarkedUnreadObsolete
|
||||
let lastDraftInteractionRowId: UInt64 = modelToCopy.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = modelToCopy.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = modelToCopy.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = modelToCopy.lastSentStoryTimestamp
|
||||
let lastVisibleSortIdObsolete: UInt64 = modelToCopy.lastVisibleSortIdObsolete
|
||||
@@ -429,6 +453,8 @@ extension TSThread: DeepCopyable {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -453,6 +479,8 @@ extension TSThread: DeepCopyable {
|
||||
let editTargetTimestamp: NSNumber? = modelToCopy.editTargetTimestamp
|
||||
let isArchivedObsolete: Bool = modelToCopy.isArchivedObsolete
|
||||
let isMarkedUnreadObsolete: Bool = modelToCopy.isMarkedUnreadObsolete
|
||||
let lastDraftInteractionRowId: UInt64 = modelToCopy.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = modelToCopy.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = modelToCopy.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = modelToCopy.lastSentStoryTimestamp
|
||||
let lastVisibleSortIdObsolete: UInt64 = modelToCopy.lastVisibleSortIdObsolete
|
||||
@@ -478,6 +506,8 @@ extension TSThread: DeepCopyable {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -500,6 +530,8 @@ extension TSThread: DeepCopyable {
|
||||
let editTargetTimestamp: NSNumber? = modelToCopy.editTargetTimestamp
|
||||
let isArchivedObsolete: Bool = modelToCopy.isArchivedObsolete
|
||||
let isMarkedUnreadObsolete: Bool = modelToCopy.isMarkedUnreadObsolete
|
||||
let lastDraftInteractionRowId: UInt64 = modelToCopy.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = modelToCopy.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = modelToCopy.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = modelToCopy.lastSentStoryTimestamp
|
||||
let lastVisibleSortIdObsolete: UInt64 = modelToCopy.lastVisibleSortIdObsolete
|
||||
@@ -527,6 +559,8 @@ extension TSThread: DeepCopyable {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -552,6 +586,8 @@ extension TSThread: DeepCopyable {
|
||||
let editTargetTimestamp: NSNumber? = modelToCopy.editTargetTimestamp
|
||||
let isArchivedObsolete: Bool = modelToCopy.isArchivedObsolete
|
||||
let isMarkedUnreadObsolete: Bool = modelToCopy.isMarkedUnreadObsolete
|
||||
let lastDraftInteractionRowId: UInt64 = modelToCopy.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = modelToCopy.lastDraftUpdateTimestamp
|
||||
let lastInteractionRowId: UInt64 = modelToCopy.lastInteractionRowId
|
||||
let lastSentStoryTimestamp: NSNumber? = modelToCopy.lastSentStoryTimestamp
|
||||
let lastVisibleSortIdObsolete: UInt64 = modelToCopy.lastVisibleSortIdObsolete
|
||||
@@ -576,6 +612,8 @@ extension TSThread: DeepCopyable {
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
@@ -625,6 +663,8 @@ extension TSThreadSerializer {
|
||||
static var addressesColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "addresses", columnType: .blob, isOptional: true) }
|
||||
static var storyViewModeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyViewMode", columnType: .int) }
|
||||
static var editTargetTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "editTargetTimestamp", columnType: .int64, isOptional: true) }
|
||||
static var lastDraftInteractionRowIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastDraftInteractionRowId", columnType: .int64) }
|
||||
static var lastDraftUpdateTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastDraftUpdateTimestamp", columnType: .int64) }
|
||||
|
||||
public static var table: SDSTableMetadata {
|
||||
SDSTableMetadata(
|
||||
@@ -656,6 +696,8 @@ extension TSThreadSerializer {
|
||||
addressesColumn,
|
||||
storyViewModeColumn,
|
||||
editTargetTimestampColumn,
|
||||
lastDraftInteractionRowIdColumn,
|
||||
lastDraftUpdateTimestampColumn,
|
||||
]
|
||||
)
|
||||
}
|
||||
@@ -1036,7 +1078,9 @@ class TSThreadSerializer: SDSSerializer {
|
||||
let addresses: Data? = nil
|
||||
let storyViewMode: UInt = model.storyViewMode.rawValue
|
||||
let editTargetTimestamp: UInt64? = archiveOptionalNSNumber(model.editTargetTimestamp, conversion: { $0.uint64Value })
|
||||
let lastDraftInteractionRowId: UInt64 = model.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = model.lastDraftUpdateTimestamp
|
||||
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp)
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,14 @@ typedef NS_CLOSED_ENUM(NSUInteger, TSThreadStoryViewMode) {
|
||||
// The corresponding interaction may have been deleted.
|
||||
@property (nonatomic) uint64_t lastInteractionRowId;
|
||||
|
||||
// These are used to maintain the ordering of drafts in the chat list.
|
||||
// When a draft is saved, the lastDraftInteractionRowId for that thread
|
||||
// should be set to the max lastInteractionRowId across all threads to
|
||||
// prioritize it in the chat list. lastDraftUpdateTimestamp
|
||||
// can be used to break ties between threads with the same lastDraftInteractionRowId.
|
||||
@property (nonatomic) uint64_t lastDraftInteractionRowId;
|
||||
@property (nonatomic) uint64_t lastDraftUpdateTimestamp;
|
||||
|
||||
@property (nonatomic, nullable) NSNumber *editTargetTimestamp;
|
||||
|
||||
@property (atomic, readonly) uint64_t mutedUntilTimestampObsolete;
|
||||
@@ -89,6 +97,8 @@ typedef NS_CLOSED_ENUM(NSUInteger, TSThreadStoryViewMode) {
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -100,7 +110,7 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:));
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastDraftInteractionRowId:lastDraftUpdateTimestamp:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -86,6 +88,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
_editTargetTimestamp = editTargetTimestamp;
|
||||
_isArchivedObsolete = isArchivedObsolete;
|
||||
_isMarkedUnreadObsolete = isMarkedUnreadObsolete;
|
||||
_lastDraftInteractionRowId = lastDraftInteractionRowId;
|
||||
_lastDraftUpdateTimestamp = lastDraftUpdateTimestamp;
|
||||
_lastInteractionRowId = lastInteractionRowId;
|
||||
_lastSentStoryTimestamp = lastSentStoryTimestamp;
|
||||
_lastVisibleSortIdObsolete = lastVisibleSortIdObsolete;
|
||||
@@ -497,6 +501,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
if (self.messageDraft == nil) {
|
||||
self.messageDraft = otherThread.messageDraft;
|
||||
self.messageDraftBodyRanges = otherThread.messageDraftBodyRanges;
|
||||
self.lastDraftInteractionRowId = otherThread.lastDraftInteractionRowId;
|
||||
self.lastDraftUpdateTimestamp = otherThread.lastDraftUpdateTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,22 @@ extension TSThread {
|
||||
editTargetTimestamp: UInt64?,
|
||||
transaction tx: DBWriteTransaction
|
||||
) {
|
||||
let mostRecentInteractionID = InteractionFinder.maxInteractionRowId(transaction: tx)
|
||||
|
||||
anyUpdate(transaction: tx) { thread in
|
||||
thread.messageDraft = draftMessageBody?.text
|
||||
thread.messageDraftBodyRanges = draftMessageBody?.ranges
|
||||
thread.editTargetTimestamp = editTargetTimestamp.map { NSNumber(value: $0) }
|
||||
|
||||
if draftMessageBody?.text.nilIfEmpty == nil {
|
||||
// 0 makes these values effectively irrelevant since they will
|
||||
// be compared to the lastInteractionRowId, which will always be > 0.
|
||||
thread.lastDraftInteractionRowId = 0
|
||||
thread.lastDraftUpdateTimestamp = 0
|
||||
} else {
|
||||
thread.lastDraftInteractionRowId = mostRecentInteractionID
|
||||
thread.lastDraftUpdateTimestamp = Date().ows_millisecondsSince1970
|
||||
}
|
||||
}
|
||||
|
||||
if let replyInfo {
|
||||
|
||||
@@ -86,7 +86,9 @@ class TSPrivateStoryThreadSerializer: SDSSerializer {
|
||||
let addresses: Data? = model.addresses
|
||||
let storyViewMode: UInt = model.storyViewMode.rawValue
|
||||
let editTargetTimestamp: UInt64? = archiveOptionalNSNumber(model.editTargetTimestamp, conversion: { $0.uint64Value })
|
||||
let lastDraftInteractionRowId: UInt64 = model.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = model.lastDraftUpdateTimestamp
|
||||
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp)
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -64,6 +66,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -78,7 +82,7 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
addresses:(nullable NSData *)addresses
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
name:(NSString *)name
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:addresses:allowsReplies:name:));
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastDraftInteractionRowId:lastDraftUpdateTimestamp:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:addresses:allowsReplies:name:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -70,6 +72,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
|
||||
@@ -86,7 +86,9 @@ class TSContactThreadSerializer: SDSSerializer {
|
||||
let addresses: Data? = nil
|
||||
let storyViewMode: UInt = model.storyViewMode.rawValue
|
||||
let editTargetTimestamp: UInt64? = archiveOptionalNSNumber(model.editTargetTimestamp, conversion: { $0.uint64Value })
|
||||
let lastDraftInteractionRowId: UInt64 = model.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = model.lastDraftUpdateTimestamp
|
||||
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp)
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -60,6 +62,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -74,7 +78,7 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
contactPhoneNumber:(nullable NSString *)contactPhoneNumber
|
||||
contactUUID:(nullable NSString *)contactUUID
|
||||
hasDismissedOffers:(BOOL)hasDismissedOffers
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:contactPhoneNumber:contactUUID:hasDismissedOffers:));
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastDraftInteractionRowId:lastDraftUpdateTimestamp:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:contactPhoneNumber:contactUUID:hasDismissedOffers:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ NSUInteger const TSContactThreadSchemaVersion = 1;
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -66,6 +68,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
|
||||
@@ -86,7 +86,9 @@ class TSGroupThreadSerializer: SDSSerializer {
|
||||
let addresses: Data? = nil
|
||||
let storyViewMode: UInt = model.storyViewMode.rawValue
|
||||
let editTargetTimestamp: UInt64? = archiveOptionalNSNumber(model.editTargetTimestamp, conversion: { $0.uint64Value })
|
||||
let lastDraftInteractionRowId: UInt64 = model.lastDraftInteractionRowId
|
||||
let lastDraftUpdateTimestamp: UInt64 = model.lastDraftUpdateTimestamp
|
||||
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp)
|
||||
return ThreadRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, conversationColorName: conversationColorName, creationDate: creationDate, isArchived: isArchived, lastInteractionRowId: lastInteractionRowId, messageDraft: messageDraft, mutedUntilDate: mutedUntilDate, shouldThreadBeVisible: shouldThreadBeVisible, contactPhoneNumber: contactPhoneNumber, contactUUID: contactUUID, groupModel: groupModel, hasDismissedOffers: hasDismissedOffers, isMarkedUnread: isMarkedUnread, lastVisibleSortIdOnScreenPercentage: lastVisibleSortIdOnScreenPercentage, lastVisibleSortId: lastVisibleSortId, messageDraftBodyRanges: messageDraftBodyRanges, mentionNotificationMode: mentionNotificationMode, mutedUntilTimestamp: mutedUntilTimestamp, allowsReplies: allowsReplies, lastSentStoryTimestamp: lastSentStoryTimestamp, name: name, addresses: addresses, storyViewMode: storyViewMode, editTargetTimestamp: editTargetTimestamp, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId;
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -59,6 +61,8 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId;
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -71,7 +75,7 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
groupModel:(TSGroupModel *)groupModel
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:groupModel:));
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:conversationColorNameObsolete:creationDate:editTargetTimestamp:isArchivedObsolete:isMarkedUnreadObsolete:lastDraftInteractionRowId:lastDraftUpdateTimestamp:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:groupModel:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific
|
||||
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
|
||||
isArchivedObsolete:(BOOL)isArchivedObsolete
|
||||
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:(uint64_t)lastInteractionRowId
|
||||
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
|
||||
@@ -49,6 +51,8 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
|
||||
@@ -206,6 +206,8 @@ extension TSGroupThread {
|
||||
editTargetTimestamp: nil,
|
||||
isArchivedObsolete: false,
|
||||
isMarkedUnreadObsolete: false,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0,
|
||||
lastInteractionRowId: 1,
|
||||
lastSentStoryTimestamp: nil,
|
||||
lastVisibleSortIdObsolete: 0,
|
||||
|
||||
@@ -221,6 +221,8 @@ final class ThreadSoftDeleteManagerImpl: ThreadSoftDeleteManager {
|
||||
/// properties on the thread.
|
||||
thread.anyUpdate(transaction: sdsTx) { thread in
|
||||
thread.lastInteractionRowId = 0
|
||||
thread.lastDraftInteractionRowId = 0
|
||||
thread.lastDraftUpdateTimestamp = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ CREATE
|
||||
,"addresses" BLOB
|
||||
,"storyViewMode" INTEGER DEFAULT 0
|
||||
,"editTargetTimestamp" INTEGER
|
||||
,"lastDraftInteractionRowId" INTEGER DEFAULT 0
|
||||
,"lastDraftUpdateTimestamp" INTEGER DEFAULT 0
|
||||
)
|
||||
;
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ public class GRDBSchemaMigrator {
|
||||
case removeAttachmentMediaTierDigestColumn
|
||||
case addListMediaTable
|
||||
case recomputeAttachmentMediaNames
|
||||
case lastDraftInteractionRowID
|
||||
|
||||
// NOTE: Every time we add a migration id, consider
|
||||
// incrementing grdbSchemaVersionLatest.
|
||||
@@ -395,7 +396,7 @@ public class GRDBSchemaMigrator {
|
||||
}
|
||||
|
||||
public static let grdbSchemaVersionDefault: UInt = 0
|
||||
public static let grdbSchemaVersionLatest: UInt = 117
|
||||
public static let grdbSchemaVersionLatest: UInt = 118
|
||||
|
||||
// An optimization for new users, we have the first migration import the latest schema
|
||||
// and mark any other migrations as "already run".
|
||||
@@ -4125,6 +4126,18 @@ public class GRDBSchemaMigrator {
|
||||
return .success(())
|
||||
}
|
||||
|
||||
migrator.registerMigration(.lastDraftInteractionRowID) { tx in
|
||||
try tx.database.alter(table: "model_TSThread") { table in
|
||||
table.add(column: "lastDraftInteractionRowId", .integer).defaults(to: 0)
|
||||
}
|
||||
|
||||
try tx.database.alter(table: "model_TSThread") { table in
|
||||
table.add(column: "lastDraftUpdateTimestamp", .integer).defaults(to: 0)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
// MARK: - Schema Migration Insertion Point
|
||||
}
|
||||
|
||||
|
||||
@@ -1589,4 +1589,20 @@ extension InteractionFinder {
|
||||
return "AND \(columnPrefix)\(interactionColumn: .editState) IS \(TSEditState.none.rawValue)"
|
||||
}
|
||||
}
|
||||
|
||||
public class func maxInteractionRowId(transaction: DBReadTransaction) -> UInt64 {
|
||||
let sql = """
|
||||
SELECT MAX(\(interactionColumn: .id))
|
||||
FROM \(InteractionRecord.databaseTableName)
|
||||
"""
|
||||
do {
|
||||
return try UInt64.fetchOne(
|
||||
transaction.database,
|
||||
sql: sql
|
||||
) ?? 0
|
||||
} catch {
|
||||
owsFailDebug("Failed to find max transaction ID: \(error)")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,4 +496,74 @@ public class ThreadFinder {
|
||||
AND \(ThreadAssociatedData.databaseTableName).isArchived = \(isArchived ? "1" : "0")
|
||||
"""
|
||||
}
|
||||
|
||||
public func internal_visibleInboxThreadIds(
|
||||
filteredBy inboxFilter: InboxFilter? = nil,
|
||||
requiredVisibleThreadIds: Set<String> = [],
|
||||
transaction: DBReadTransaction
|
||||
) throws -> [String] {
|
||||
if inboxFilter == .unread {
|
||||
let sql = """
|
||||
SELECT
|
||||
\(threadColumnFullyQualified: .uniqueId) AS thread_uniqueId,
|
||||
\(ThreadAssociatedData.databaseTableName).isMarkedUnread AS thread_isMarkedUnread,
|
||||
COUNT(i.\(interactionColumn: .uniqueId)) AS interactions_unreadCount
|
||||
FROM \(ThreadRecord.databaseTableName)
|
||||
INNER JOIN \(ThreadAssociatedData.databaseTableName)
|
||||
ON \(ThreadAssociatedData.databaseTableName).threadUniqueId = \(threadColumnFullyQualified: .uniqueId)
|
||||
AND \(ThreadAssociatedData.databaseTableName).isArchived = 0
|
||||
LEFT OUTER JOIN \(InteractionRecord.databaseTableName) AS i
|
||||
\(DEBUG_INDEXED_BY("index_model_TSInteraction_UnreadMessages"))
|
||||
ON i.\(interactionColumn: .threadUniqueId) = thread_uniqueId
|
||||
AND \(InteractionFinder.sqlClauseForUnreadInteractionCounts(interactionsAlias: "i"))
|
||||
WHERE \(threadColumnFullyQualified: .shouldThreadBeVisible) = 1
|
||||
GROUP BY thread_uniqueId
|
||||
HAVING (
|
||||
thread_isMarkedUnread = 1
|
||||
OR interactions_unreadCount > 0
|
||||
\(requiredVisibleThreadsClause(forThreadIds: requiredVisibleThreadIds))
|
||||
)
|
||||
ORDER BY
|
||||
CASE WHEN \(threadColumn: .lastDraftInteractionRowId) > \(threadColumn: .lastInteractionRowId)
|
||||
THEN \(threadColumn: .lastDraftInteractionRowId) ELSE \(threadColumn: .lastInteractionRowId)
|
||||
END DESC,
|
||||
\(threadColumn: .lastDraftUpdateTimestamp) DESC
|
||||
"""
|
||||
|
||||
return try String.fetchAll(transaction.database, sql: sql, adapter: RangeRowAdapter(0..<1))
|
||||
} else {
|
||||
let sql = """
|
||||
SELECT \(threadColumn: .uniqueId)
|
||||
FROM \(ThreadRecord.databaseTableName)
|
||||
INNER JOIN \(ThreadAssociatedData.databaseTableName)
|
||||
ON \(ThreadAssociatedData.databaseTableName).threadUniqueId = \(threadColumnFullyQualified: .uniqueId)
|
||||
AND \(ThreadAssociatedData.databaseTableName).isArchived = 0
|
||||
WHERE \(threadColumn: .shouldThreadBeVisible) = 1
|
||||
ORDER BY
|
||||
CASE WHEN \(threadColumn: .lastDraftInteractionRowId) > \(threadColumn: .lastInteractionRowId)
|
||||
THEN \(threadColumn: .lastDraftInteractionRowId) ELSE \(threadColumn: .lastInteractionRowId)
|
||||
END DESC,
|
||||
\(threadColumn: .lastDraftUpdateTimestamp) DESC
|
||||
"""
|
||||
return try String.fetchAll(transaction.database, sql: sql)
|
||||
}
|
||||
}
|
||||
|
||||
public func internal_visibleArchivedThreadIds(
|
||||
transaction: DBReadTransaction
|
||||
) throws -> [String] {
|
||||
let sql = """
|
||||
SELECT \(threadColumn: .uniqueId)
|
||||
FROM \(ThreadRecord.databaseTableName)
|
||||
\(threadAssociatedDataJoinClause(isArchived: true))
|
||||
WHERE \(threadColumn: .shouldThreadBeVisible) = 1
|
||||
ORDER BY
|
||||
CASE WHEN \(threadColumn: .lastDraftInteractionRowId) > \(threadColumn: .lastInteractionRowId)
|
||||
THEN \(threadColumn: .lastDraftInteractionRowId) ELSE \(threadColumn: .lastInteractionRowId)
|
||||
END DESC,
|
||||
\(threadColumn: .lastDraftUpdateTimestamp) DESC
|
||||
"""
|
||||
|
||||
return try String.fetchAll(transaction.database, sql: sql)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ public enum FeatureFlags {
|
||||
public static let libsignalEnforceMinTlsVersion = false
|
||||
|
||||
public static let notificationServiceWebSocket = build.includes(.internal)
|
||||
|
||||
public static let moveDraftsUpChatList = build.includes(.internal)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@@ -25,6 +25,8 @@ class EditManagerTests: SSKBaseTest {
|
||||
editTargetTimestamp: nil,
|
||||
isArchivedObsolete: false,
|
||||
isMarkedUnreadObsolete: false,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0,
|
||||
lastInteractionRowId: 0,
|
||||
lastSentStoryTimestamp: nil,
|
||||
lastVisibleSortIdObsolete: 0,
|
||||
|
||||
354
SignalServiceKit/tests/Messages/ThreadFinderTests.swift
Normal file
354
SignalServiceKit/tests/Messages/ThreadFinderTests.swift
Normal file
@@ -0,0 +1,354 @@
|
||||
//
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import LibSignalClient
|
||||
|
||||
@testable import SignalServiceKit
|
||||
|
||||
class ThreadFinderTests: XCTestCase {
|
||||
private var db = InMemoryDB()
|
||||
private let threadFinder = ThreadFinder()
|
||||
private var contactThread1: TSContactThread!
|
||||
private var contactThread2: TSContactThread!
|
||||
|
||||
enum ChatListType: CaseIterable {
|
||||
case inbox
|
||||
case unread
|
||||
case archive
|
||||
}
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
let testPhone1 = E164("+16505550101")!
|
||||
let testACI1 = Aci.constantForTesting("00000000-0000-4000-8000-00000000000A")
|
||||
contactThread1 = TSContactThread(contactAddress: SignalServiceAddress(
|
||||
serviceId: testACI1,
|
||||
phoneNumber: testPhone1.stringValue,
|
||||
cache: SignalServiceAddressCache()
|
||||
))
|
||||
|
||||
let testPhone2 = E164("+16505550100")!
|
||||
let testACI2 = Aci.constantForTesting("00000000-0000-4000-A000-00000000000B")
|
||||
contactThread2 = TSContactThread(contactAddress: SignalServiceAddress(
|
||||
serviceId: testACI2,
|
||||
phoneNumber: testPhone2.stringValue,
|
||||
cache: SignalServiceAddressCache()
|
||||
))
|
||||
}
|
||||
|
||||
func buildThreadRecord(
|
||||
uniqueID: String,
|
||||
contactThread: TSContactThread,
|
||||
draft: String?,
|
||||
lastInteractionRowID: UInt64,
|
||||
lastDraftInteractionRowId: UInt64,
|
||||
lastDraftUpdateTimestamp: UInt64
|
||||
) -> ThreadRecord {
|
||||
ThreadRecord(
|
||||
delegate: contactThread,
|
||||
recordType: .contactThread,
|
||||
uniqueId: uniqueID,
|
||||
conversationColorName: "Obsolete",
|
||||
creationDate: Date.now.timeIntervalSince1970,
|
||||
isArchived: false,
|
||||
lastInteractionRowId: lastInteractionRowID,
|
||||
messageDraft: draft,
|
||||
mutedUntilDate: nil,
|
||||
shouldThreadBeVisible: true,
|
||||
contactPhoneNumber: nil,
|
||||
contactUUID: nil,
|
||||
groupModel: nil,
|
||||
hasDismissedOffers: false,
|
||||
isMarkedUnread: false,
|
||||
lastVisibleSortIdOnScreenPercentage: 0.0,
|
||||
lastVisibleSortId: 0,
|
||||
messageDraftBodyRanges: nil,
|
||||
mentionNotificationMode: 0,
|
||||
mutedUntilTimestamp: 0,
|
||||
allowsReplies: nil,
|
||||
lastSentStoryTimestamp: nil,
|
||||
name: nil,
|
||||
addresses: nil,
|
||||
storyViewMode: 0,
|
||||
editTargetTimestamp: nil,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
|
||||
func buildThreadAssociatedData(
|
||||
uniqueID: String,
|
||||
isMarkedUnread: Bool,
|
||||
isArchived: Bool
|
||||
) -> ThreadAssociatedData {
|
||||
return ThreadAssociatedData(
|
||||
threadUniqueId: uniqueID,
|
||||
isArchived: isArchived,
|
||||
isMarkedUnread: isMarkedUnread,
|
||||
mutedUntilTimestamp: 0,
|
||||
audioPlaybackRate: 1
|
||||
)
|
||||
}
|
||||
|
||||
func newDraftGoesToTop(chatListType: ChatListType) throws {
|
||||
try db.write { transaction in
|
||||
let database = transaction.database
|
||||
|
||||
// New draft.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 0,
|
||||
lastDraftInteractionRowId: 1,
|
||||
lastDraftUpdateTimestamp: 1
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID1",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
|
||||
// Non-draft that has more recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID2",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
}
|
||||
|
||||
switch chatListType {
|
||||
case .inbox, .unread:
|
||||
try db.read { transaction in
|
||||
let messages = try threadFinder.internal_visibleInboxThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(messages.count, 2)
|
||||
XCTAssertTrue(messages.first == "UUID1", "First message should be the draft")
|
||||
}
|
||||
case .archive:
|
||||
try db.read { transaction in
|
||||
let messages = try threadFinder.internal_visibleArchivedThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(messages.count, 2)
|
||||
XCTAssertTrue(messages.first == "UUID1", "First message should be the draft")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testNewDraftGoesToTop_inbox() throws {
|
||||
try newDraftGoesToTop(chatListType: .inbox)
|
||||
}
|
||||
|
||||
func testNewDraftGoesToTop_unread() throws {
|
||||
try newDraftGoesToTop(chatListType: .unread)
|
||||
}
|
||||
|
||||
func testNewDraftGoesToTop_archive() throws {
|
||||
try newDraftGoesToTop(chatListType: .archive)
|
||||
}
|
||||
|
||||
func ignoreDraftRowIdThatIsNotMostRecent(chatListType: ChatListType) throws {
|
||||
try db.write { transaction in
|
||||
let database = transaction.database
|
||||
|
||||
// New draft that is not the latest activity on the thread.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 3,
|
||||
lastDraftInteractionRowId: 1,
|
||||
lastDraftUpdateTimestamp: 1
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID1",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
|
||||
// Non-draft that has less recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 2,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID2",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
}
|
||||
|
||||
switch chatListType {
|
||||
case .inbox, .unread:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleInboxThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID1", "First thread should be the draft thread, even though the draft is not the most recent activity")
|
||||
}
|
||||
case .archive:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleArchivedThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID1", "First thread should be the draft thread, even though the draft is not the most recent activity")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testIgnoreDraftRowIdThatIsNotMostRecent_inbox() throws {
|
||||
try ignoreDraftRowIdThatIsNotMostRecent(chatListType: .inbox)
|
||||
}
|
||||
|
||||
func testIgnoreDraftRowIdThatIsNotMostRecent_unread() throws {
|
||||
try ignoreDraftRowIdThatIsNotMostRecent(chatListType: .unread)
|
||||
}
|
||||
|
||||
func testIgnoreDraftRowIdThatIsNotMostRecent_archive() throws {
|
||||
try ignoreDraftRowIdThatIsNotMostRecent(chatListType: .archive)
|
||||
}
|
||||
|
||||
func sameDraftRowIdFallsBackToTimestamp(chatListType: ChatListType) throws {
|
||||
try db.write { transaction in
|
||||
let database = transaction.database
|
||||
|
||||
// Thread 1, has a draft after latest TSInteraction, but less recent than UUID2.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 2,
|
||||
lastDraftInteractionRowId: 2,
|
||||
lastDraftUpdateTimestamp: 1
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID1",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
|
||||
// Thread 2, has a more recent draft based on timestamp.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 2,
|
||||
lastDraftUpdateTimestamp: 2
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID2",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
}
|
||||
|
||||
switch chatListType {
|
||||
case .inbox, .unread:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleInboxThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID2", "First thread should be the one with latest timestamp")
|
||||
}
|
||||
case .archive:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleArchivedThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID2", "First thread should be the one with latest timestamp")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSameDraftRowIdFallsBackToTimestamp_inbox() throws {
|
||||
try sameDraftRowIdFallsBackToTimestamp(chatListType: .inbox)
|
||||
}
|
||||
|
||||
func testSameDraftRowIdFallsBackToTimestamp_unread() throws {
|
||||
try sameDraftRowIdFallsBackToTimestamp(chatListType: .unread)
|
||||
}
|
||||
|
||||
func testSameDraftRowIdFallsBackToTimestamp_archive() throws {
|
||||
try sameDraftRowIdFallsBackToTimestamp(chatListType: .archive)
|
||||
}
|
||||
|
||||
func draftNotMostRecent(chatListType: ChatListType) throws {
|
||||
try db.write { transaction in
|
||||
let database = transaction.database
|
||||
|
||||
// New draft that is not the latest activity on the thread.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 2,
|
||||
lastDraftUpdateTimestamp: 100
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID1",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
|
||||
// Non-draft that has less recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 3,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0
|
||||
).insert(database)
|
||||
|
||||
try buildThreadAssociatedData(
|
||||
uniqueID: "UUID2",
|
||||
isMarkedUnread: chatListType == .unread,
|
||||
isArchived: chatListType == .archive
|
||||
).insert(database)
|
||||
}
|
||||
|
||||
switch chatListType {
|
||||
case .inbox, .unread:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleInboxThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID2", "First thread should be most recent thread, the non-draft")
|
||||
}
|
||||
case .archive:
|
||||
try db.read { transaction in
|
||||
let threads = try threadFinder.internal_visibleArchivedThreadIds(transaction: transaction)
|
||||
XCTAssertEqual(threads.count, 2)
|
||||
XCTAssertTrue(threads.first == "UUID2", "First thread should be most recent thread, the non-draft")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testDraftNotMostRecent_inbox() throws {
|
||||
try draftNotMostRecent(chatListType: .inbox)
|
||||
}
|
||||
|
||||
func testDraftNotMostRecent_unread() throws {
|
||||
try draftNotMostRecent(chatListType: .unread)
|
||||
}
|
||||
|
||||
func testDraftNotMostRecent_archive() throws {
|
||||
try draftNotMostRecent(chatListType: .archive)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user