Uncodegen TSThread
This commit is contained in:
parent
214599386b
commit
5d47b5d0e8
@ -1,6 +1,5 @@
|
||||
{
|
||||
"nsnumber_types": {
|
||||
"TSThread.archivedAsOfMessageSortId": "UInt64",
|
||||
"TSIncomingMessage.serverTimestamp": "UInt64",
|
||||
"TSIncomingMessage.deprecated_sourceDeviceId": "UInt32",
|
||||
"TSOutgoingMessageRecipientState.deliveryTimestamp": "UInt64",
|
||||
@ -8,17 +7,11 @@
|
||||
"OWSBackupFragment.uncompressedDataLength": "UInt64",
|
||||
"SSKJobRecord.exclusiveProcessIdentifier": "Int32",
|
||||
"TSMessage.storyTimestamp": "UInt64",
|
||||
"TSThread.editTargetTimestamp": "UInt64",
|
||||
"TSThread.lastSentStoryTimestamp": "UInt64",
|
||||
"TSThread.lastViewedStoryTimestamp": "UInt64",
|
||||
"TSThread.lastReceivedStoryTimestamp": "UInt64",
|
||||
"TSMessage.expireTimerVersion": "UInt32"
|
||||
"TSMessage.expireTimerVersion": "UInt32"
|
||||
},
|
||||
"properties_to_ignore": [
|
||||
"TSYapDatabaseObject.grdbId",
|
||||
"OWSDynamicOutgoingMessage.block",
|
||||
"TSThread.isArchivedByLegacyTimestampForSorting",
|
||||
"TSContactThread.contactThreadSchemaVersion",
|
||||
"TSCall.callSchemaVersion",
|
||||
"TSErrorMessage.errorMessageSchemaVersion",
|
||||
"TSMessage.schemaVersion",
|
||||
@ -32,11 +25,9 @@
|
||||
"TSOutgoingMessage.changeActionsProtoData"
|
||||
],
|
||||
"class_cache_get_code": {
|
||||
"TSThread": "SSKEnvironment.shared.modelReadCachesRef.threadReadCache.getThread(uniqueId: uniqueId, transaction: transaction)",
|
||||
"TSInteraction": "SSKEnvironment.shared.modelReadCachesRef.interactionReadCache.getInteraction(uniqueId: uniqueId, transaction: transaction)"
|
||||
},
|
||||
"class_cache_set_code": {
|
||||
"TSThread": "SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didReadThread",
|
||||
"TSInteraction": "SSKEnvironment.shared.modelReadCachesRef.interactionReadCache.didReadInteraction"
|
||||
},
|
||||
"class_to_skip_serialization": [
|
||||
@ -62,16 +53,6 @@
|
||||
"TSOutgoingDeleteMessage"
|
||||
],
|
||||
"manually_typed_fields": {
|
||||
"TSThread.conversationColorName": {
|
||||
"swift_type": "ConversationColorName",
|
||||
"objc_initializer_type": "ConversationColorName",
|
||||
"is_objc_codable": true,
|
||||
"is_enum": true,
|
||||
"column_type": ".unicodeString",
|
||||
"record_swift_type": "String",
|
||||
"serialize_record_invocation": "%s.rawValue",
|
||||
"should_use_blob": false
|
||||
},
|
||||
"TSInfoMessage.infoMessageUserInfo": {
|
||||
"swift_type": "[InfoMessageUserInfoKey: AnyObject]",
|
||||
"objc_initializer_type": "NSDictionary<InfoMessageUserInfoKey, id> *",
|
||||
@ -81,26 +62,6 @@
|
||||
"record_swift_type": "Data",
|
||||
"serialize_record_invocation": "optionalArchive(%s)",
|
||||
"should_use_blob": true
|
||||
},
|
||||
"TSThread.mentionNotificationMode": {
|
||||
"swift_type": "TSThreadMentionNotificationMode",
|
||||
"objc_initializer_type": "TSThreadMentionNotificationMode",
|
||||
"is_objc_codable": true,
|
||||
"is_enum": true,
|
||||
"column_type": ".int",
|
||||
"record_swift_type": "UInt",
|
||||
"serialize_record_invocation": "%s.rawValue",
|
||||
"should_use_blob": false
|
||||
},
|
||||
"TSThread.storyViewMode": {
|
||||
"swift_type": "TSThreadStoryViewMode",
|
||||
"objc_initializer_type": "TSThreadStoryViewMode",
|
||||
"is_objc_codable": true,
|
||||
"is_enum": true,
|
||||
"column_type": ".int",
|
||||
"record_swift_type": "UInt",
|
||||
"serialize_record_invocation": "%s.rawValue",
|
||||
"should_use_blob": false
|
||||
}
|
||||
},
|
||||
"custom_accessors": {
|
||||
@ -112,26 +73,11 @@
|
||||
"TSIncomingMessage.viewed": "wasViewed"
|
||||
},
|
||||
"custom_column_names": {
|
||||
"TSInteraction.uniqueThreadId": "threadUniqueId",
|
||||
"TSThread.lastVisibleSortIdObsolete": "lastVisibleSortId",
|
||||
"TSThread.lastVisibleSortIdOnScreenPercentageObsolete": "lastVisibleSortIdOnScreenPercentage",
|
||||
"TSThread.mutedUntilDateObsolete": "mutedUntilDate",
|
||||
"TSThread.isArchivedObsolete": "isArchived",
|
||||
"TSThread.isMarkedUnreadObsolete": "isMarkedUnread",
|
||||
"TSThread.mutedUntilTimestampObsolete": "mutedUntilTimestamp",
|
||||
"TSThread.conversationColorNameObsolete": "conversationColorName"
|
||||
"TSInteraction.uniqueThreadId": "threadUniqueId"
|
||||
},
|
||||
"aliased_column_names": {
|
||||
"TSInteraction.sortId": "id",
|
||||
"SSKJobRecord.sortId": "id"
|
||||
},
|
||||
"renamed_column_names": {
|
||||
"TSThread.lastVisibleSortIdObsolete": "lastVisibleSortId",
|
||||
"TSThread.lastVisibleSortIdOnScreenPercentageObsolete": "lastVisibleSortIdOnScreenPercentage",
|
||||
"TSThread.mutedUntilDateObsolete": "mutedUntilDate",
|
||||
"TSThread.isArchivedObsolete": "isArchived",
|
||||
"TSThread.isMarkedUnreadObsolete": "isMarkedUnread",
|
||||
"TSThread.mutedUntilTimestampObsolete": "mutedUntilTimestamp",
|
||||
"TSThread.conversationColorNameObsolete": "conversationColorName"
|
||||
}
|
||||
"renamed_column_names": {}
|
||||
}
|
||||
|
||||
@ -301,7 +301,6 @@ class ParsedClass:
|
||||
#
|
||||
# Except the special-cased ones.
|
||||
force_optional = property.type_info().is_enum
|
||||
force_optional = force_optional and property.name not in ["mentionNotificationMode", "storyViewMode"]
|
||||
property.force_optional = force_optional
|
||||
record_properties.append(property)
|
||||
for property in subclass_properties:
|
||||
@ -485,25 +484,7 @@ class TypeInfo:
|
||||
# Do nothing; we don't need to unpack this non-optional.
|
||||
pass
|
||||
|
||||
if value_name == "mentionNotificationMode":
|
||||
value_statement = (
|
||||
"let %s: %s = TSThreadMentionNotificationMode(rawValue: %s) ?? .default"
|
||||
% (
|
||||
value_name,
|
||||
"TSThreadMentionNotificationMode",
|
||||
value_expr,
|
||||
)
|
||||
)
|
||||
elif value_name == "storyViewMode":
|
||||
value_statement = (
|
||||
"let %s: %s = TSThreadStoryViewMode(rawValue: %s) ?? .default"
|
||||
% (
|
||||
value_name,
|
||||
"TSThreadStoryViewMode",
|
||||
value_expr,
|
||||
)
|
||||
)
|
||||
elif self.is_codable:
|
||||
if self.is_codable:
|
||||
value_statement = "let %s: %s = %s" % (
|
||||
value_name,
|
||||
initializer_param_type,
|
||||
@ -1167,7 +1148,7 @@ def generate_swift_extensions_for_model(clazz):
|
||||
return
|
||||
|
||||
has_sds_superclass = clazz.has_sds_superclass()
|
||||
has_remove_methods = clazz.name not in ("TSThread", "TSInteraction")
|
||||
has_remove_methods = clazz.name not in ("TSInteraction")
|
||||
has_grdb_serializer = clazz.name in ("TSInteraction")
|
||||
|
||||
swift_filename = os.path.basename(clazz.filepath)
|
||||
@ -1362,8 +1343,7 @@ public extension %s {
|
||||
property_name = property.column_name()
|
||||
swift_type = type_info.swift_type()
|
||||
|
||||
did_force_optional = property.name not in ["mentionNotificationMode", "storyViewMode"]
|
||||
did_force_optional = did_force_optional and type_info.is_enum
|
||||
did_force_optional = type_info.is_enum
|
||||
|
||||
if property_name == "recordType":
|
||||
# recordType is an enum, but its property info here doesn't
|
||||
@ -1463,8 +1443,7 @@ extension %s {
|
||||
value_name = "%s" % property.name
|
||||
|
||||
if property.name not in ("uniqueId",):
|
||||
did_force_optional = property.name not in ["mentionNotificationMode", "storyViewMode"]
|
||||
did_force_optional = did_force_optional and property.name not in base_property_names
|
||||
did_force_optional = property.name not in base_property_names
|
||||
did_force_optional = did_force_optional and not property.is_optional
|
||||
did_force_optional = did_force_optional or property.type_info().is_enum
|
||||
for statement in property.deserialize_record_invocation(
|
||||
@ -1784,8 +1763,7 @@ extension %(class_name)s: DeepCopyable {
|
||||
for property in deserialize_properties:
|
||||
value_name = "%s" % property.name
|
||||
|
||||
did_force_optional = property.name not in ["mentionNotificationMode", "storyViewMode"]
|
||||
did_force_optional = did_force_optional and property.name not in base_property_names
|
||||
did_force_optional = property.name not in base_property_names
|
||||
did_force_optional = did_force_optional and not property.is_optional
|
||||
did_force_optional = did_force_optional or property.type_info().is_enum
|
||||
for statement in property.deep_copy_record_invocation(
|
||||
|
||||
@ -99,7 +99,6 @@
|
||||
05104E3A2C8B541000F8851F /* AccessibleLayoutMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */; };
|
||||
0510F69E2C91EB3000FA3FDE /* ScrollBounceBehaviorIfAvailable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */; };
|
||||
0512145B2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0512145A2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift */; };
|
||||
0517B9782BFCFF12002CDE7D /* TSThreadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0517B9772BFCFF12002CDE7D /* TSThreadTests.swift */; };
|
||||
052647BF2C63BAD60076E99D /* ChatListFilterControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052647BE2C63BAC40076E99D /* ChatListFilterControl.swift */; };
|
||||
052647C12C6404DD0076E99D /* ChatListFilterStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052647C02C6404D70076E99D /* ChatListFilterStore.swift */; };
|
||||
052A33382C52BF410083D812 /* ChatListFilterActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052A33372C52BF410083D812 /* ChatListFilterActions.swift */; };
|
||||
@ -3799,29 +3798,17 @@
|
||||
F9C5CCA4289453B300548EEE /* SSKProto+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9B9289453B100548EEE /* SSKProto+OWS.swift */; };
|
||||
F9C5CCAC289453B300548EEE /* PreKeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9C2289453B100548EEE /* PreKeyManager.swift */; };
|
||||
F9C5CCB0289453B300548EEE /* RemoteAttestation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9C7289453B100548EEE /* RemoteAttestation.swift */; };
|
||||
F9C5CCBA289453B300548EEE /* TSThread.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C9D2289453B100548EEE /* TSThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CCC0289453B300548EEE /* ContactDiscoveryTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9D9289453B100548EEE /* ContactDiscoveryTask.swift */; };
|
||||
F9C5CCC3289453B300548EEE /* ContactDiscoveryError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9DC289453B100548EEE /* ContactDiscoveryError.swift */; };
|
||||
F9C5CCC5289453B300548EEE /* SignalAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9DE289453B100548EEE /* SignalAccount.swift */; };
|
||||
F9C5CCC8289453B300548EEE /* SignalRecipient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E1289453B100548EEE /* SignalRecipient.swift */; };
|
||||
F9C5CCCA289453B300548EEE /* TSPrivateStoryThread+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E4289453B100548EEE /* TSPrivateStoryThread+SDS.swift */; };
|
||||
F9C5CCCB289453B300548EEE /* TSPrivateStoryThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E5289453B100548EEE /* TSPrivateStoryThread.swift */; };
|
||||
F9C5CCCC289453B300548EEE /* TSPrivateStoryThread.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C9E6289453B100548EEE /* TSPrivateStoryThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CCCD289453B300548EEE /* TSGroupThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E7289453B100548EEE /* TSGroupThread.m */; };
|
||||
F9C5CCCE289453B300548EEE /* TSGroupThread+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E8289453B100548EEE /* TSGroupThread+SDS.swift */; };
|
||||
F9C5CCCF289453B300548EEE /* TSThread+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9E9289453B100548EEE /* TSThread+OWS.swift */; };
|
||||
F9C5CCD0289453B300548EEE /* TSContactThread+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9EA289453B100548EEE /* TSContactThread+SDS.swift */; };
|
||||
F9C5CCD1289453B300548EEE /* TSContactThread.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C9EB289453B100548EEE /* TSContactThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CCD2289453B300548EEE /* TSGroupThread.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C9EC289453B100548EEE /* TSGroupThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CCD3289453B300548EEE /* TSPrivateStoryThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9ED289453B100548EEE /* TSPrivateStoryThread.m */; };
|
||||
F9C5CCD4289453B300548EEE /* TSContactThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9EE289453B100548EEE /* TSContactThread.m */; };
|
||||
F9C5CCD5289453B300548EEE /* TSGroupThread+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9EF289453B100548EEE /* TSGroupThread+OWS.swift */; };
|
||||
F9C5CCD6289453B300548EEE /* PhoneNumberUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F0289453B100548EEE /* PhoneNumberUtil.swift */; };
|
||||
F9C5CCD8289453B300548EEE /* ThreadAssociatedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F2289453B100548EEE /* ThreadAssociatedData.swift */; };
|
||||
F9C5CCDA289453B300548EEE /* DisappearingMessagesConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F4289453B100548EEE /* DisappearingMessagesConfigurationRecord.swift */; };
|
||||
F9C5CCDD289453B300548EEE /* SignalAccountFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F7289453B100548EEE /* SignalAccountFinder.swift */; };
|
||||
F9C5CCDE289453B300548EEE /* TSThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F8289453B100548EEE /* TSThread.m */; };
|
||||
F9C5CCDF289453B300548EEE /* TSThread+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9F9289453B100548EEE /* TSThread+SDS.swift */; };
|
||||
F9C5CCE1289453B300548EEE /* ContactThreadFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9FB289453B100548EEE /* ContactThreadFinder.swift */; };
|
||||
F9C5CCE2289453B300548EEE /* SignalServiceAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9FC289453B100548EEE /* SignalServiceAddress.swift */; };
|
||||
F9C5CCE3289453B300548EEE /* TSGroupMember.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C9FD289453B100548EEE /* TSGroupMember.swift */; };
|
||||
@ -4242,7 +4229,6 @@
|
||||
05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibleLayoutMetric.swift; sourceTree = "<group>"; };
|
||||
0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollBounceBehaviorIfAvailable.swift; sourceTree = "<group>"; };
|
||||
0512145A2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionDifference+SSK.swift"; sourceTree = "<group>"; };
|
||||
0517B9772BFCFF12002CDE7D /* TSThreadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThreadTests.swift; sourceTree = "<group>"; };
|
||||
052647BE2C63BAC40076E99D /* ChatListFilterControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterControl.swift; sourceTree = "<group>"; };
|
||||
052647C02C6404D70076E99D /* ChatListFilterStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterStore.swift; sourceTree = "<group>"; };
|
||||
052A33372C52BF410083D812 /* ChatListFilterActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterActions.swift; sourceTree = "<group>"; };
|
||||
@ -8104,29 +8090,17 @@
|
||||
F9C5C9B9289453B100548EEE /* SSKProto+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SSKProto+OWS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9C2289453B100548EEE /* PreKeyManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreKeyManager.swift; sourceTree = "<group>"; };
|
||||
F9C5C9C7289453B100548EEE /* RemoteAttestation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteAttestation.swift; sourceTree = "<group>"; };
|
||||
F9C5C9D2289453B100548EEE /* TSThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSThread.h; sourceTree = "<group>"; };
|
||||
F9C5C9D9289453B100548EEE /* ContactDiscoveryTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactDiscoveryTask.swift; sourceTree = "<group>"; };
|
||||
F9C5C9DC289453B100548EEE /* ContactDiscoveryError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactDiscoveryError.swift; sourceTree = "<group>"; };
|
||||
F9C5C9DE289453B100548EEE /* SignalAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalAccount.swift; sourceTree = "<group>"; };
|
||||
F9C5C9E1289453B100548EEE /* SignalRecipient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalRecipient.swift; sourceTree = "<group>"; };
|
||||
F9C5C9E4289453B100548EEE /* TSPrivateStoryThread+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSPrivateStoryThread+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9E5289453B100548EEE /* TSPrivateStoryThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSPrivateStoryThread.swift; sourceTree = "<group>"; };
|
||||
F9C5C9E6289453B100548EEE /* TSPrivateStoryThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPrivateStoryThread.h; sourceTree = "<group>"; };
|
||||
F9C5C9E7289453B100548EEE /* TSGroupThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupThread.m; sourceTree = "<group>"; };
|
||||
F9C5C9E8289453B100548EEE /* TSGroupThread+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSGroupThread+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9E9289453B100548EEE /* TSThread+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSThread+OWS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9EA289453B100548EEE /* TSContactThread+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSContactThread+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9EB289453B100548EEE /* TSContactThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSContactThread.h; sourceTree = "<group>"; };
|
||||
F9C5C9EC289453B100548EEE /* TSGroupThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupThread.h; sourceTree = "<group>"; };
|
||||
F9C5C9ED289453B100548EEE /* TSPrivateStoryThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPrivateStoryThread.m; sourceTree = "<group>"; };
|
||||
F9C5C9EE289453B100548EEE /* TSContactThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSContactThread.m; sourceTree = "<group>"; };
|
||||
F9C5C9EF289453B100548EEE /* TSGroupThread+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSGroupThread+OWS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9F0289453B100548EEE /* PhoneNumberUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumberUtil.swift; sourceTree = "<group>"; };
|
||||
F9C5C9F2289453B100548EEE /* ThreadAssociatedData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadAssociatedData.swift; sourceTree = "<group>"; };
|
||||
F9C5C9F4289453B100548EEE /* DisappearingMessagesConfigurationRecord.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisappearingMessagesConfigurationRecord.swift; sourceTree = "<group>"; };
|
||||
F9C5C9F7289453B100548EEE /* SignalAccountFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalAccountFinder.swift; sourceTree = "<group>"; };
|
||||
F9C5C9F8289453B100548EEE /* TSThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThread.m; sourceTree = "<group>"; };
|
||||
F9C5C9F9289453B100548EEE /* TSThread+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TSThread+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C9FB289453B100548EEE /* ContactThreadFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactThreadFinder.swift; sourceTree = "<group>"; };
|
||||
F9C5C9FC289453B100548EEE /* SignalServiceAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalServiceAddress.swift; sourceTree = "<group>"; };
|
||||
F9C5C9FD289453B100548EEE /* TSGroupMember.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSGroupMember.swift; sourceTree = "<group>"; };
|
||||
@ -14666,7 +14640,6 @@
|
||||
F94261E4289B1B5400460798 /* Util */,
|
||||
724D47B12B97BE73001BE973 /* ZkParams */,
|
||||
F942623A289B1B5500460798 /* SSKBaseTest.swift */,
|
||||
0517B9772BFCFF12002CDE7D /* TSThreadTests.swift */,
|
||||
D9C964072BE44D510058F143 /* XCTest+Thenable.swift */,
|
||||
);
|
||||
path = tests;
|
||||
@ -15075,9 +15048,6 @@
|
||||
5033D45E29D4DAAC007FEADA /* ThreadMerger.swift */,
|
||||
45161BA828A2E54B0055AB45 /* ThreadReplyInfo.swift */,
|
||||
502D45472A0AD7BE00B8BCE0 /* ThreadReplyInfoStore.swift */,
|
||||
F9C5C9F9289453B100548EEE /* TSThread+SDS.swift */,
|
||||
F9C5C9D2289453B100548EEE /* TSThread.h */,
|
||||
F9C5C9F8289453B100548EEE /* TSThread.m */,
|
||||
D91444E62CDD72BE00221D81 /* TSThread.swift */,
|
||||
50D6A93E2AA9167400B7F093 /* UniqueObjectRecipientMerger.swift */,
|
||||
5037F1932A43A6A300C372AD /* UserProfileMerger.swift */,
|
||||
@ -15105,18 +15075,9 @@
|
||||
502D45432A05A34B00B8BCE0 /* ThreadRemover.swift */,
|
||||
D9F9A63E2C013EF100EF13EC /* ThreadSoftDeleteManager.swift */,
|
||||
5033D46029D638FD007FEADA /* ThreadStore.swift */,
|
||||
F9C5C9EA289453B100548EEE /* TSContactThread+SDS.swift */,
|
||||
F9C5C9EB289453B100548EEE /* TSContactThread.h */,
|
||||
F9C5C9EE289453B100548EEE /* TSContactThread.m */,
|
||||
661170C92ABA522200A1B16D /* TSContactThread.swift */,
|
||||
F9C5C9EF289453B100548EEE /* TSGroupThread+OWS.swift */,
|
||||
F9C5C9E8289453B100548EEE /* TSGroupThread+SDS.swift */,
|
||||
F9C5C9EC289453B100548EEE /* TSGroupThread.h */,
|
||||
F9C5C9E7289453B100548EEE /* TSGroupThread.m */,
|
||||
880FB40528CD205F00FA1C10 /* TSGroupThread.swift */,
|
||||
F9C5C9E4289453B100548EEE /* TSPrivateStoryThread+SDS.swift */,
|
||||
F9C5C9E6289453B100548EEE /* TSPrivateStoryThread.h */,
|
||||
F9C5C9ED289453B100548EEE /* TSPrivateStoryThread.m */,
|
||||
F9C5C9E5289453B100548EEE /* TSPrivateStoryThread.swift */,
|
||||
F9C5C9E9289453B100548EEE /* TSThread+OWS.swift */,
|
||||
);
|
||||
@ -15645,10 +15606,8 @@
|
||||
F9C5CD2C289453B300548EEE /* SSKAccessors+SDS.h in Headers */,
|
||||
668A01142C2B6077007B8808 /* Threading.h in Headers */,
|
||||
F9C5CBC8289453B300548EEE /* TSCall.h in Headers */,
|
||||
F9C5CCD1289453B300548EEE /* TSContactThread.h in Headers */,
|
||||
F9C5CBDD289453B300548EEE /* TSErrorMessage.h in Headers */,
|
||||
F9C5CE74289453B400548EEE /* TSGroupModel.h in Headers */,
|
||||
F9C5CCD2289453B300548EEE /* TSGroupThread.h in Headers */,
|
||||
F9C5CBDC289453B300548EEE /* TSIncomingMessage.h in Headers */,
|
||||
F9C5CBED289453B300548EEE /* TSInfoMessage.h in Headers */,
|
||||
F9C5CBF2289453B300548EEE /* TSInteraction.h in Headers */,
|
||||
@ -15658,9 +15617,7 @@
|
||||
F9C5CBE0289453B300548EEE /* TSMessage.h in Headers */,
|
||||
F9C5CBD2289453B300548EEE /* TSOutgoingMessage.h in Headers */,
|
||||
F9C5CD82289453B300548EEE /* TSPaymentModels.h in Headers */,
|
||||
F9C5CCCC289453B300548EEE /* TSPrivateStoryThread.h in Headers */,
|
||||
F9C5CC03289453B300548EEE /* TSQuotedMessage.h in Headers */,
|
||||
F9C5CCBA289453B300548EEE /* TSThread.h in Headers */,
|
||||
F9C5CBF1289453B300548EEE /* TSUnreadIndicatorInteraction.h in Headers */,
|
||||
F9C5CD61289453B300548EEE /* TSYapDatabaseObject.h in Headers */,
|
||||
);
|
||||
@ -19935,8 +19892,6 @@
|
||||
F9C5CC5D289453B300548EEE /* TSCall.m in Sources */,
|
||||
D91AC93E2B6337B200814975 /* TSCall.swift in Sources */,
|
||||
F9C5CE67289453B400548EEE /* TSConstants.swift in Sources */,
|
||||
F9C5CCD0289453B300548EEE /* TSContactThread+SDS.swift in Sources */,
|
||||
F9C5CCD4289453B300548EEE /* TSContactThread.m in Sources */,
|
||||
661170CA2ABA522200A1B16D /* TSContactThread.swift in Sources */,
|
||||
D93086292C61672E008E3A27 /* TSErrorMessage+Builder.swift in Sources */,
|
||||
F9C5CBE1289453B300548EEE /* TSErrorMessage+SDS.swift in Sources */,
|
||||
@ -19948,8 +19903,6 @@
|
||||
F9C5CE79289453B400548EEE /* TSGroupModel.swift in Sources */,
|
||||
F9C5CE75289453B400548EEE /* TSGroupModelBuilder.swift in Sources */,
|
||||
F9C5CCD5289453B300548EEE /* TSGroupThread+OWS.swift in Sources */,
|
||||
F9C5CCCE289453B300548EEE /* TSGroupThread+SDS.swift in Sources */,
|
||||
F9C5CCCD289453B300548EEE /* TSGroupThread.m in Sources */,
|
||||
880FB40628CD205F00FA1C10 /* TSGroupThread.swift in Sources */,
|
||||
F9C5CBDB289453B300548EEE /* TSIncomingMessage+Builder.swift in Sources */,
|
||||
F9C5CBD5289453B300548EEE /* TSIncomingMessage+SDS.swift in Sources */,
|
||||
@ -19995,16 +19948,12 @@
|
||||
F9C5CD8A289453B300548EEE /* TSPaymentModels.m in Sources */,
|
||||
F9C5CD83289453B300548EEE /* TSPaymentModels.swift in Sources */,
|
||||
6642A87C2A8EB0D800E591C2 /* TSPaymentsActivationRequestModel.swift in Sources */,
|
||||
F9C5CCCA289453B300548EEE /* TSPrivateStoryThread+SDS.swift in Sources */,
|
||||
F9C5CCD3289453B300548EEE /* TSPrivateStoryThread.m in Sources */,
|
||||
F9C5CCCB289453B300548EEE /* TSPrivateStoryThread.swift in Sources */,
|
||||
F9C5CBE4289453B300548EEE /* TSQuotedMessage.m in Sources */,
|
||||
6615553F2ABA5A7500AA302B /* TSRegistrationState.swift in Sources */,
|
||||
66C2B1312A05D28A008DDE72 /* TSRequest.swift in Sources */,
|
||||
6691E7EF2996E8FB0032A68A /* TSRequestOWSURLSessionMock.swift in Sources */,
|
||||
F9C5CCCF289453B300548EEE /* TSThread+OWS.swift in Sources */,
|
||||
F9C5CCDF289453B300548EEE /* TSThread+SDS.swift in Sources */,
|
||||
F9C5CCDE289453B300548EEE /* TSThread.m in Sources */,
|
||||
D91444E72CDD72C200221D81 /* TSThread.swift in Sources */,
|
||||
F9C5CBE3289453B300548EEE /* TSUnreadIndicatorInteraction+SDS.swift in Sources */,
|
||||
F9C5CBD9289453B300548EEE /* TSUnreadIndicatorInteraction.m in Sources */,
|
||||
@ -20296,7 +20245,6 @@
|
||||
F942628A289B1B5600460798 /* TSMessageTest.swift in Sources */,
|
||||
D9495A702C76965600843BC1 /* TSOutgoingMessageRecipientStateTest.swift in Sources */,
|
||||
F9426289289B1B5600460798 /* TSOutgoingMessageTest.swift in Sources */,
|
||||
0517B9782BFCFF12002CDE7D /* TSThreadTests.swift in Sources */,
|
||||
F942628F289B1B5600460798 /* TypingIndicatorMessageTest.swift in Sources */,
|
||||
726C94252D6F48DA00ABF9B9 /* UncooperativeTimeoutTest.swift in Sources */,
|
||||
F9426255289B1B5500460798 /* UnfairLockTest.swift in Sources */,
|
||||
|
||||
@ -350,7 +350,7 @@ extension ConversationViewController: ConversationInputToolbarDelegate {
|
||||
|
||||
if
|
||||
let threadTimestamp = thread.editTargetTimestamp,
|
||||
threadTimestamp.uint64Value != editTarget?.timestamp ?? 0
|
||||
threadTimestamp != editTarget?.timestamp ?? 0
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ class MyStoriesViewController: OWSViewController, FailedStorySendDisplayControll
|
||||
StoryManager.storyName(for: rhs),
|
||||
) == .orderedAscending
|
||||
}
|
||||
return (lhs.lastSentStoryTimestamp?.uint64Value ?? 0) > (rhs.lastSentStoryTimestamp?.uint64Value ?? 0)
|
||||
return (lhs.lastSentStoryTimestamp ?? 0) > (rhs.lastSentStoryTimestamp ?? 0)
|
||||
})
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@ -78,9 +78,9 @@ class MockConversationView: UIView {
|
||||
}()
|
||||
|
||||
// Use a v5 UUID that's in a separate namespace from ACIs/PNIs.
|
||||
fileprivate static let mockAddress = SignalServiceAddress(try! ServiceId.parseFrom(serviceIdString: "00000000-0000-5000-8000-000000000000"))
|
||||
fileprivate static let mockAci = try! Aci.parseFrom(serviceIdString: "00000000-0000-5000-8000-000000000000")
|
||||
|
||||
private let thread = MockThread(contactAddress: MockConversationView.mockAddress)
|
||||
private let thread = MockThread(contactAci: MockConversationView.mockAci)
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
@ -211,28 +211,39 @@ class MockConversationView: UIView {
|
||||
// MARK: - Mock Classes
|
||||
|
||||
private class MockThread: TSContactThread {
|
||||
override var shouldBeSaved: Bool {
|
||||
return false
|
||||
init(contactAci: Aci) {
|
||||
super.init(
|
||||
uniqueId: "MockThread",
|
||||
contactUUID: contactAci.serviceIdUppercaseString,
|
||||
contactPhoneNumber: nil,
|
||||
)
|
||||
}
|
||||
|
||||
override var uniqueId: String { "MockThread" }
|
||||
required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
owsFail("not supported")
|
||||
}
|
||||
|
||||
override func anyWillInsert(with transaction: DBWriteTransaction) {
|
||||
override func anyWillInsert(transaction: DBWriteTransaction) {
|
||||
// no - op
|
||||
owsFailDebug("shouldn't save mock thread")
|
||||
owsFail("shouldn't save mock thread")
|
||||
}
|
||||
}
|
||||
|
||||
public class MockGroupThread: TSGroupThread {
|
||||
override public var shouldBeSaved: Bool {
|
||||
return false
|
||||
class MockGroupThread: TSGroupThread {
|
||||
init(groupModel: TSGroupModelV2) {
|
||||
super.init(
|
||||
uniqueId: "MockGroupThread",
|
||||
groupModel: groupModel,
|
||||
)
|
||||
}
|
||||
|
||||
override public var uniqueId: String { "MockGroupThread" }
|
||||
required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
owsFail("not supported")
|
||||
}
|
||||
|
||||
override public func anyWillInsert(with transaction: DBWriteTransaction) {
|
||||
override func anyWillInsert(transaction: DBWriteTransaction) {
|
||||
// no - op
|
||||
owsFailDebug("shouldn't save mock thread")
|
||||
owsFail("shouldn't save mock thread")
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +307,7 @@ public class MockOutgoingMessage: TSOutgoingMessage {
|
||||
|
||||
override public func readRecipientAddresses() -> [SignalServiceAddress] {
|
||||
// makes message appear as read
|
||||
return [MockConversationView.mockAddress]
|
||||
return [SignalServiceAddress(MockConversationView.mockAci)]
|
||||
}
|
||||
|
||||
override public func recipientState(for recipientAddress: SignalServiceAddress) -> TSOutgoingMessageRecipientState? {
|
||||
|
||||
@ -56,8 +56,7 @@ public final class BackupArchiveThreadStore {
|
||||
context: BackupArchive.ChatRestoringContext,
|
||||
) throws -> TSContactThread {
|
||||
let thread = TSContactThread(contactAddress: context.recipientContext.localIdentifiers.aciAddress)
|
||||
let record = thread.asRecord()
|
||||
try record.insert(context.tx.database)
|
||||
try thread.insert(context.tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
@ -66,8 +65,7 @@ public final class BackupArchiveThreadStore {
|
||||
context: BackupArchive.ChatRestoringContext,
|
||||
) throws -> TSContactThread {
|
||||
let thread = TSContactThread(contactAddress: address.asInteropAddress())
|
||||
let record = thread.asRecord()
|
||||
try record.insert(context.tx.database)
|
||||
try thread.insert(context.tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
@ -85,8 +83,7 @@ public final class BackupArchiveThreadStore {
|
||||
default:
|
||||
groupThread.storyViewMode = .default
|
||||
}
|
||||
let record = groupThread.asRecord()
|
||||
try record.insert(context.tx.database)
|
||||
try groupThread.insert(context.tx.database)
|
||||
return groupThread
|
||||
}
|
||||
|
||||
@ -127,9 +124,9 @@ public final class BackupArchiveThreadStore {
|
||||
sql: """
|
||||
UPDATE \(TSThread.databaseTableName)
|
||||
SET
|
||||
\(TSThreadSerializer.mentionNotificationModeColumn.columnName) = ?
|
||||
\(threadColumn: .mentionNotificationMode) = ?
|
||||
WHERE
|
||||
\(TSThreadSerializer.idColumn.columnName) = ?;
|
||||
\(threadColumn: .id) = ?;
|
||||
""",
|
||||
arguments: [TSThreadMentionNotificationMode.never.rawValue, thread.threadRowId],
|
||||
)
|
||||
@ -144,10 +141,10 @@ public final class BackupArchiveThreadStore {
|
||||
sql: """
|
||||
UPDATE \(TSThread.databaseTableName)
|
||||
SET
|
||||
\(TSThreadSerializer.shouldThreadBeVisibleColumn.columnName) = 1,
|
||||
\(TSThreadSerializer.lastInteractionRowIdColumn.columnName) = ?
|
||||
\(threadColumn: .shouldThreadBeVisible) = 1,
|
||||
\(threadColumn: .lastInteractionRowId) = ?
|
||||
WHERE
|
||||
\(TSThreadSerializer.idColumn.columnName) = ?;
|
||||
\(threadColumn: .id) = ?;
|
||||
""",
|
||||
arguments: [lastInteractionRowId ?? 0, thread.threadRowId],
|
||||
)
|
||||
|
||||
@ -49,7 +49,6 @@ public final class BackupArchiveStoryStore {
|
||||
allowsReplies: allowReplies,
|
||||
viewMode: viewMode,
|
||||
)
|
||||
var record = myStory.asRecord()
|
||||
|
||||
let existingMyStoryRowId = try Int64.fetchOne(
|
||||
context.tx.database,
|
||||
@ -59,12 +58,11 @@ public final class BackupArchiveStoryStore {
|
||||
arguments: [TSPrivateStoryThread.myStoryUniqueId],
|
||||
)
|
||||
if let existingMyStoryRowId {
|
||||
record.id = existingMyStoryRowId
|
||||
myStory.updateRowId(existingMyStoryRowId)
|
||||
myStory.id = existingMyStoryRowId
|
||||
}
|
||||
|
||||
// Use save to insert or update as my story might already exist.
|
||||
try record.save(context.tx.database)
|
||||
try myStory.save(context.tx.database)
|
||||
return myStory
|
||||
}
|
||||
|
||||
@ -80,8 +78,7 @@ public final class BackupArchiveStoryStore {
|
||||
_ storyThread: TSPrivateStoryThread,
|
||||
context: BackupArchive.RecipientRestoringContext,
|
||||
) throws {
|
||||
let record = storyThread.asRecord()
|
||||
try record.insert(context.tx.database)
|
||||
try storyThread.insert(context.tx.database)
|
||||
}
|
||||
|
||||
func createStoryContextAssociatedData(
|
||||
|
||||
@ -21,7 +21,7 @@ public struct BackupArchiveExportProgress {
|
||||
// all weighted evenly. Its just an estimate.
|
||||
return try
|
||||
SignalRecipient.fetchCount(tx.database)
|
||||
+ ThreadRecord.fetchCount(tx.database)
|
||||
+ TSThread.fetchCount(tx.database)
|
||||
+ InteractionRecord.fetchCount(tx.database)
|
||||
+ CallLinkRecord.fetchCount(tx.database)
|
||||
+ StickerPackRecord.fetchCount(tx.database)
|
||||
|
||||
@ -253,7 +253,7 @@ class BackupAttachmentDownloadStoreTests: XCTestCase {
|
||||
|
||||
private func insertThread(tx: DBWriteTransaction) -> TSThread {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
|
||||
@ -327,7 +327,7 @@ class BackupAttachmentUploadStoreTests: XCTestCase {
|
||||
|
||||
private func insertThread(tx: DBWriteTransaction) -> TSThread {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
|
||||
@ -323,7 +323,7 @@ public class BackupListMediaManagerTests {
|
||||
tx: DBWriteTransaction,
|
||||
) -> Attachment.IDType {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
let attachmentParams = Attachment.ConstructionParams.mockStream(
|
||||
mediaName: mediaName,
|
||||
)
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "OWSGroupCallMessage.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSCall.h"
|
||||
#import "TSContactThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -21,12 +21,12 @@ public class ContactThreadFinder: NSObject {
|
||||
|
||||
func contactThreads(for serviceId: ServiceId, tx: DBReadTransaction) -> [TSContactThread] {
|
||||
let serviceIdString = serviceId.serviceIdUppercaseString
|
||||
let sql = "SELECT * FROM \(TSThread.databaseTableName) WHERE \(threadColumn: .contactUUID) = ?"
|
||||
let sql = "SELECT * FROM \(TSThread.databaseTableName) WHERE \(contactThreadColumn: .contactUUID) = ?"
|
||||
return fetchContactThreads(sql: sql, arguments: [serviceIdString], tx: tx)
|
||||
}
|
||||
|
||||
func contactThreads(for phoneNumber: String, tx: DBReadTransaction) -> [TSContactThread] {
|
||||
let sql = "SELECT * FROM \(TSThread.databaseTableName) WHERE \(threadColumn: .contactPhoneNumber) = ?"
|
||||
let sql = "SELECT * FROM \(TSThread.databaseTableName) WHERE \(contactThreadColumn: .contactPhoneNumber) = ?"
|
||||
return fetchContactThreads(sql: sql, arguments: [phoneNumber], tx: tx)
|
||||
}
|
||||
|
||||
|
||||
@ -1,998 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public import GRDB
|
||||
|
||||
// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
|
||||
// Do not manually edit it, instead run `sds_codegen.sh`.
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
public struct ThreadRecord: SDSRecord {
|
||||
public weak var delegate: SDSRecordDelegate?
|
||||
|
||||
public var tableMetadata: SDSTableMetadata {
|
||||
TSThreadSerializer.table
|
||||
}
|
||||
|
||||
public static var databaseTableName: String {
|
||||
TSThreadSerializer.table.tableName
|
||||
}
|
||||
|
||||
public var id: Int64?
|
||||
|
||||
// This defines all of the columns used in the table
|
||||
// where this model (and any subclasses) are persisted.
|
||||
public let recordType: SDSRecordType?
|
||||
public let uniqueId: String
|
||||
|
||||
// Properties
|
||||
public let conversationColorName: String
|
||||
public let creationDate: Double?
|
||||
public let isArchived: Bool
|
||||
public let lastInteractionRowId: UInt64
|
||||
public let messageDraft: String?
|
||||
public let mutedUntilDate: Double?
|
||||
public let shouldThreadBeVisible: Bool
|
||||
public let contactPhoneNumber: String?
|
||||
public let contactUUID: String?
|
||||
public let groupModel: Data?
|
||||
public let hasDismissedOffers: Bool?
|
||||
public let isMarkedUnread: Bool
|
||||
public let lastVisibleSortIdOnScreenPercentage: Double
|
||||
public let lastVisibleSortId: UInt64
|
||||
public let messageDraftBodyRanges: Data?
|
||||
public let mentionNotificationMode: UInt
|
||||
public let mutedUntilTimestamp: UInt64
|
||||
public let allowsReplies: Bool?
|
||||
public let lastSentStoryTimestamp: UInt64?
|
||||
public let name: String?
|
||||
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
|
||||
case recordType
|
||||
case uniqueId
|
||||
case conversationColorName
|
||||
case creationDate
|
||||
case isArchived
|
||||
case lastInteractionRowId
|
||||
case messageDraft
|
||||
case mutedUntilDate
|
||||
case shouldThreadBeVisible
|
||||
case contactPhoneNumber
|
||||
case contactUUID
|
||||
case groupModel
|
||||
case hasDismissedOffers
|
||||
case isMarkedUnread
|
||||
case lastVisibleSortIdOnScreenPercentage
|
||||
case lastVisibleSortId
|
||||
case messageDraftBodyRanges
|
||||
case mentionNotificationMode
|
||||
case mutedUntilTimestamp
|
||||
case allowsReplies
|
||||
case lastSentStoryTimestamp
|
||||
case name
|
||||
case addresses
|
||||
case storyViewMode
|
||||
case editTargetTimestamp
|
||||
case lastDraftInteractionRowId
|
||||
case lastDraftUpdateTimestamp
|
||||
}
|
||||
|
||||
public static func columnName(_ column: ThreadRecord.CodingKeys, fullyQualified: Bool = false) -> String {
|
||||
fullyQualified ? "\(databaseTableName).\(column.rawValue)" : column.rawValue
|
||||
}
|
||||
|
||||
public func didInsert(with rowID: Int64, for column: String?) {
|
||||
guard let delegate = delegate else {
|
||||
owsFailDebug("Missing delegate.")
|
||||
return
|
||||
}
|
||||
delegate.updateRowId(rowID)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Row Initializer
|
||||
|
||||
public extension ThreadRecord {
|
||||
static var databaseSelection: [SQLSelectable] {
|
||||
CodingKeys.allCases
|
||||
}
|
||||
|
||||
init(row: Row) {
|
||||
id = row[0]
|
||||
recordType = row[1].flatMap { SDSRecordType(rawValue: $0) }
|
||||
uniqueId = row[2]
|
||||
conversationColorName = row[3]
|
||||
creationDate = row[4]
|
||||
isArchived = row[5]
|
||||
lastInteractionRowId = row[6]
|
||||
messageDraft = row[7]
|
||||
mutedUntilDate = row[8]
|
||||
shouldThreadBeVisible = row[9]
|
||||
contactPhoneNumber = row[10]
|
||||
contactUUID = row[11]
|
||||
groupModel = row[12]
|
||||
hasDismissedOffers = row[13]
|
||||
isMarkedUnread = row[14]
|
||||
lastVisibleSortIdOnScreenPercentage = row[15]
|
||||
lastVisibleSortId = row[16]
|
||||
messageDraftBodyRanges = row[17]
|
||||
mentionNotificationMode = row[18]
|
||||
mutedUntilTimestamp = row[19]
|
||||
allowsReplies = row[20]
|
||||
lastSentStoryTimestamp = row[21]
|
||||
name = row[22]
|
||||
addresses = row[23]
|
||||
storyViewMode = row[24]
|
||||
editTargetTimestamp = row[25]
|
||||
lastDraftInteractionRowId = row[26]
|
||||
lastDraftUpdateTimestamp = row[27]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StringInterpolation
|
||||
|
||||
public extension String.StringInterpolation {
|
||||
mutating func appendInterpolation(threadColumn column: ThreadRecord.CodingKeys) {
|
||||
appendLiteral(ThreadRecord.columnName(column))
|
||||
}
|
||||
mutating func appendInterpolation(threadColumnFullyQualified column: ThreadRecord.CodingKeys) {
|
||||
appendLiteral(ThreadRecord.columnName(column, fullyQualified: true))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Deserialization
|
||||
|
||||
extension TSThread {
|
||||
// This method defines how to deserialize a model, given a
|
||||
// database row. The recordType column is used to determine
|
||||
// the corresponding model class.
|
||||
class func fromRecord(_ record: ThreadRecord) throws -> TSThread {
|
||||
|
||||
guard let recordId = record.id else { throw SDSError.missingRequiredField(fieldName: "id") }
|
||||
guard let recordType = record.recordType else { throw SDSError.missingRequiredField(fieldName: "recordType") }
|
||||
|
||||
switch recordType {
|
||||
case .contactThread:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let conversationColorNameObsolete: String = record.conversationColorName
|
||||
let creationDateInterval: Double? = record.creationDate
|
||||
let creationDate: Date? = SDSDeserialization.optionalDoubleAsDate(creationDateInterval, name: "creationDate")
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = record.lastVisibleSortIdOnScreenPercentage
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = TSThreadMentionNotificationMode(rawValue: record.mentionNotificationMode) ?? .default
|
||||
let messageDraft: String? = record.messageDraft
|
||||
let messageDraftBodyRangesSerialized: Data? = record.messageDraftBodyRanges
|
||||
let messageDraftBodyRanges: MessageBodyRanges? = try messageDraftBodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
|
||||
let mutedUntilDateObsoleteInterval: Double? = record.mutedUntilDate
|
||||
let mutedUntilDateObsolete: Date? = SDSDeserialization.optionalDoubleAsDate(mutedUntilDateObsoleteInterval, name: "mutedUntilDateObsolete")
|
||||
let mutedUntilTimestampObsolete: UInt64 = record.mutedUntilTimestamp
|
||||
let shouldThreadBeVisible: Bool = record.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = TSThreadStoryViewMode(rawValue: record.storyViewMode) ?? .default
|
||||
let contactPhoneNumber: String? = record.contactPhoneNumber
|
||||
let contactUUID: String? = record.contactUUID
|
||||
let hasDismissedOffers: Bool = try SDSDeserialization.required(record.hasDismissedOffers, name: "hasDismissedOffers")
|
||||
|
||||
return TSContactThread(grdbId: recordId,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
contactPhoneNumber: contactPhoneNumber,
|
||||
contactUUID: contactUUID,
|
||||
hasDismissedOffers: hasDismissedOffers)
|
||||
|
||||
case .groupThread:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let conversationColorNameObsolete: String = record.conversationColorName
|
||||
let creationDateInterval: Double? = record.creationDate
|
||||
let creationDate: Date? = SDSDeserialization.optionalDoubleAsDate(creationDateInterval, name: "creationDate")
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = record.lastVisibleSortIdOnScreenPercentage
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = TSThreadMentionNotificationMode(rawValue: record.mentionNotificationMode) ?? .default
|
||||
let messageDraft: String? = record.messageDraft
|
||||
let messageDraftBodyRangesSerialized: Data? = record.messageDraftBodyRanges
|
||||
let messageDraftBodyRanges: MessageBodyRanges? = try messageDraftBodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
|
||||
let mutedUntilDateObsoleteInterval: Double? = record.mutedUntilDate
|
||||
let mutedUntilDateObsolete: Date? = SDSDeserialization.optionalDoubleAsDate(mutedUntilDateObsoleteInterval, name: "mutedUntilDateObsolete")
|
||||
let mutedUntilTimestampObsolete: UInt64 = record.mutedUntilTimestamp
|
||||
let shouldThreadBeVisible: Bool = record.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = TSThreadStoryViewMode(rawValue: record.storyViewMode) ?? .default
|
||||
let groupModelSerialized: Data = try record.groupModel ?? { () -> Data in throw SDSError.missingRequiredField(fieldName: "groupModel") }()
|
||||
let groupModel: TSGroupModel = try SDSDeserialization.unarchivedObject(ofClass: TSGroupModel.self, from: groupModelSerialized)
|
||||
|
||||
return TSGroupThread(grdbId: recordId,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
groupModel: groupModel)
|
||||
|
||||
case .privateStoryThread:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let conversationColorNameObsolete: String = record.conversationColorName
|
||||
let creationDateInterval: Double? = record.creationDate
|
||||
let creationDate: Date? = SDSDeserialization.optionalDoubleAsDate(creationDateInterval, name: "creationDate")
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = record.lastVisibleSortIdOnScreenPercentage
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = TSThreadMentionNotificationMode(rawValue: record.mentionNotificationMode) ?? .default
|
||||
let messageDraft: String? = record.messageDraft
|
||||
let messageDraftBodyRangesSerialized: Data? = record.messageDraftBodyRanges
|
||||
let messageDraftBodyRanges: MessageBodyRanges? = try messageDraftBodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
|
||||
let mutedUntilDateObsoleteInterval: Double? = record.mutedUntilDate
|
||||
let mutedUntilDateObsolete: Date? = SDSDeserialization.optionalDoubleAsDate(mutedUntilDateObsoleteInterval, name: "mutedUntilDateObsolete")
|
||||
let mutedUntilTimestampObsolete: UInt64 = record.mutedUntilTimestamp
|
||||
let shouldThreadBeVisible: Bool = record.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = TSThreadStoryViewMode(rawValue: record.storyViewMode) ?? .default
|
||||
let addresses: Data? = SDSDeserialization.optionalData(record.addresses, name: "addresses")
|
||||
let allowsReplies: Bool = try SDSDeserialization.required(record.allowsReplies, name: "allowsReplies")
|
||||
let name: String = try SDSDeserialization.required(record.name, name: "name")
|
||||
|
||||
return TSPrivateStoryThread(grdbId: recordId,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
addresses: addresses,
|
||||
allowsReplies: allowsReplies,
|
||||
name: name)
|
||||
|
||||
case .thread:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let conversationColorNameObsolete: String = record.conversationColorName
|
||||
let creationDateInterval: Double? = record.creationDate
|
||||
let creationDate: Date? = SDSDeserialization.optionalDoubleAsDate(creationDateInterval, name: "creationDate")
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = record.lastVisibleSortIdOnScreenPercentage
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = TSThreadMentionNotificationMode(rawValue: record.mentionNotificationMode) ?? .default
|
||||
let messageDraft: String? = record.messageDraft
|
||||
let messageDraftBodyRangesSerialized: Data? = record.messageDraftBodyRanges
|
||||
let messageDraftBodyRanges: MessageBodyRanges? = try messageDraftBodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
|
||||
let mutedUntilDateObsoleteInterval: Double? = record.mutedUntilDate
|
||||
let mutedUntilDateObsolete: Date? = SDSDeserialization.optionalDoubleAsDate(mutedUntilDateObsoleteInterval, name: "mutedUntilDateObsolete")
|
||||
let mutedUntilTimestampObsolete: UInt64 = record.mutedUntilTimestamp
|
||||
let shouldThreadBeVisible: Bool = record.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = TSThreadStoryViewMode(rawValue: record.storyViewMode) ?? .default
|
||||
|
||||
return TSThread(grdbId: recordId,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode)
|
||||
|
||||
default:
|
||||
owsFailDebug("Unexpected record type: \(recordType)")
|
||||
throw SDSError.invalidValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSModel
|
||||
|
||||
extension TSThread: SDSModel {
|
||||
public var serializer: SDSSerializer {
|
||||
// Any subclass can be cast to it's superclass,
|
||||
// so the order of this switch statement matters.
|
||||
// We need to do a "depth first" search by type.
|
||||
switch self {
|
||||
case let model as TSPrivateStoryThread:
|
||||
assert(type(of: model) == TSPrivateStoryThread.self)
|
||||
return TSPrivateStoryThreadSerializer(model: model)
|
||||
case let model as TSGroupThread:
|
||||
assert(type(of: model) == TSGroupThread.self)
|
||||
return TSGroupThreadSerializer(model: model)
|
||||
case let model as TSContactThread:
|
||||
assert(type(of: model) == TSContactThread.self)
|
||||
return TSContactThreadSerializer(model: model)
|
||||
default:
|
||||
return TSThreadSerializer(model: self)
|
||||
}
|
||||
}
|
||||
|
||||
public func asRecord() -> SDSRecord {
|
||||
serializer.asRecord()
|
||||
}
|
||||
|
||||
public var sdsTableName: String {
|
||||
ThreadRecord.databaseTableName
|
||||
}
|
||||
|
||||
public static var table: SDSTableMetadata {
|
||||
TSThreadSerializer.table
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeepCopyable
|
||||
|
||||
extension TSThread: DeepCopyable {
|
||||
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
guard let id = self.grdbId?.int64Value else {
|
||||
throw OWSAssertionError("Model missing grdbId.")
|
||||
}
|
||||
|
||||
// Any subclass can be cast to its superclass, so the order of these if
|
||||
// statements matters. We need to do a "depth first" search by type.
|
||||
|
||||
if let modelToCopy = self as? TSPrivateStoryThread {
|
||||
assert(type(of: modelToCopy) == TSPrivateStoryThread.self)
|
||||
let uniqueId: String = modelToCopy.uniqueId
|
||||
let conversationColorNameObsolete: String = modelToCopy.conversationColorNameObsolete
|
||||
let creationDate: Date? = modelToCopy.creationDate
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = modelToCopy.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = modelToCopy.mentionNotificationMode
|
||||
let messageDraft: String? = modelToCopy.messageDraft
|
||||
let messageDraftBodyRanges: MessageBodyRanges?
|
||||
if let messageDraftBodyRangesForCopy = modelToCopy.messageDraftBodyRanges {
|
||||
messageDraftBodyRanges = try DeepCopies.deepCopy(messageDraftBodyRangesForCopy)
|
||||
} else {
|
||||
messageDraftBodyRanges = nil
|
||||
}
|
||||
let mutedUntilDateObsolete: Date? = modelToCopy.mutedUntilDateObsolete
|
||||
let mutedUntilTimestampObsolete: UInt64 = modelToCopy.mutedUntilTimestampObsolete
|
||||
let shouldThreadBeVisible: Bool = modelToCopy.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = modelToCopy.storyViewMode
|
||||
let addresses: Data? = modelToCopy.addresses
|
||||
let allowsReplies: Bool = modelToCopy.allowsReplies
|
||||
let name: String = modelToCopy.name
|
||||
|
||||
return TSPrivateStoryThread(grdbId: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
addresses: addresses,
|
||||
allowsReplies: allowsReplies,
|
||||
name: name)
|
||||
}
|
||||
|
||||
if let modelToCopy = self as? TSGroupThread {
|
||||
assert(type(of: modelToCopy) == TSGroupThread.self)
|
||||
let uniqueId: String = modelToCopy.uniqueId
|
||||
let conversationColorNameObsolete: String = modelToCopy.conversationColorNameObsolete
|
||||
let creationDate: Date? = modelToCopy.creationDate
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = modelToCopy.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = modelToCopy.mentionNotificationMode
|
||||
let messageDraft: String? = modelToCopy.messageDraft
|
||||
let messageDraftBodyRanges: MessageBodyRanges?
|
||||
if let messageDraftBodyRangesForCopy = modelToCopy.messageDraftBodyRanges {
|
||||
messageDraftBodyRanges = try DeepCopies.deepCopy(messageDraftBodyRangesForCopy)
|
||||
} else {
|
||||
messageDraftBodyRanges = nil
|
||||
}
|
||||
let mutedUntilDateObsolete: Date? = modelToCopy.mutedUntilDateObsolete
|
||||
let mutedUntilTimestampObsolete: UInt64 = modelToCopy.mutedUntilTimestampObsolete
|
||||
let shouldThreadBeVisible: Bool = modelToCopy.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = modelToCopy.storyViewMode
|
||||
let groupModel: TSGroupModel = try DeepCopies.deepCopy(modelToCopy.groupModel)
|
||||
|
||||
return TSGroupThread(grdbId: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
groupModel: groupModel)
|
||||
}
|
||||
|
||||
if let modelToCopy = self as? TSContactThread {
|
||||
assert(type(of: modelToCopy) == TSContactThread.self)
|
||||
let uniqueId: String = modelToCopy.uniqueId
|
||||
let conversationColorNameObsolete: String = modelToCopy.conversationColorNameObsolete
|
||||
let creationDate: Date? = modelToCopy.creationDate
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = modelToCopy.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = modelToCopy.mentionNotificationMode
|
||||
let messageDraft: String? = modelToCopy.messageDraft
|
||||
let messageDraftBodyRanges: MessageBodyRanges?
|
||||
if let messageDraftBodyRangesForCopy = modelToCopy.messageDraftBodyRanges {
|
||||
messageDraftBodyRanges = try DeepCopies.deepCopy(messageDraftBodyRangesForCopy)
|
||||
} else {
|
||||
messageDraftBodyRanges = nil
|
||||
}
|
||||
let mutedUntilDateObsolete: Date? = modelToCopy.mutedUntilDateObsolete
|
||||
let mutedUntilTimestampObsolete: UInt64 = modelToCopy.mutedUntilTimestampObsolete
|
||||
let shouldThreadBeVisible: Bool = modelToCopy.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = modelToCopy.storyViewMode
|
||||
let contactPhoneNumber: String? = modelToCopy.contactPhoneNumber
|
||||
let contactUUID: String? = modelToCopy.contactUUID
|
||||
let hasDismissedOffers: Bool = modelToCopy.hasDismissedOffers
|
||||
|
||||
return TSContactThread(grdbId: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
contactPhoneNumber: contactPhoneNumber,
|
||||
contactUUID: contactUUID,
|
||||
hasDismissedOffers: hasDismissedOffers)
|
||||
}
|
||||
|
||||
do {
|
||||
let modelToCopy = self
|
||||
assert(type(of: modelToCopy) == TSThread.self)
|
||||
let uniqueId: String = modelToCopy.uniqueId
|
||||
let conversationColorNameObsolete: String = modelToCopy.conversationColorNameObsolete
|
||||
let creationDate: Date? = modelToCopy.creationDate
|
||||
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
|
||||
let lastVisibleSortIdOnScreenPercentageObsolete: Double = modelToCopy.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let mentionNotificationMode: TSThreadMentionNotificationMode = modelToCopy.mentionNotificationMode
|
||||
let messageDraft: String? = modelToCopy.messageDraft
|
||||
let messageDraftBodyRanges: MessageBodyRanges?
|
||||
if let messageDraftBodyRangesForCopy = modelToCopy.messageDraftBodyRanges {
|
||||
messageDraftBodyRanges = try DeepCopies.deepCopy(messageDraftBodyRangesForCopy)
|
||||
} else {
|
||||
messageDraftBodyRanges = nil
|
||||
}
|
||||
let mutedUntilDateObsolete: Date? = modelToCopy.mutedUntilDateObsolete
|
||||
let mutedUntilTimestampObsolete: UInt64 = modelToCopy.mutedUntilTimestampObsolete
|
||||
let shouldThreadBeVisible: Bool = modelToCopy.shouldThreadBeVisible
|
||||
let storyViewMode: TSThreadStoryViewMode = modelToCopy.storyViewMode
|
||||
|
||||
return TSThread(grdbId: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table Metadata
|
||||
|
||||
extension TSThreadSerializer {
|
||||
|
||||
// This defines all of the columns used in the table
|
||||
// where this model (and any subclasses) are persisted.
|
||||
static var idColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "id", columnType: .primaryKey) }
|
||||
static var recordTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "recordType", columnType: .int64) }
|
||||
static var uniqueIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "uniqueId", columnType: .unicodeString, isUnique: true) }
|
||||
// Properties
|
||||
static var conversationColorNameObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "conversationColorNameObsolete", columnType: .unicodeString) }
|
||||
static var creationDateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "creationDate", columnType: .double, isOptional: true) }
|
||||
static var isArchivedObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isArchivedObsolete", columnType: .int) }
|
||||
static var lastInteractionRowIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastInteractionRowId", columnType: .int64) }
|
||||
static var messageDraftColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "messageDraft", columnType: .unicodeString, isOptional: true) }
|
||||
static var mutedUntilDateObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "mutedUntilDateObsolete", columnType: .double, isOptional: true) }
|
||||
static var shouldThreadBeVisibleColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "shouldThreadBeVisible", columnType: .int) }
|
||||
static var contactPhoneNumberColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "contactPhoneNumber", columnType: .unicodeString, isOptional: true) }
|
||||
static var contactUUIDColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "contactUUID", columnType: .unicodeString, isOptional: true) }
|
||||
static var groupModelColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "groupModel", columnType: .blob, isOptional: true) }
|
||||
static var hasDismissedOffersColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "hasDismissedOffers", columnType: .int, isOptional: true) }
|
||||
static var isMarkedUnreadObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isMarkedUnreadObsolete", columnType: .int) }
|
||||
static var lastVisibleSortIdOnScreenPercentageObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastVisibleSortIdOnScreenPercentageObsolete", columnType: .double) }
|
||||
static var lastVisibleSortIdObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastVisibleSortIdObsolete", columnType: .int64) }
|
||||
static var messageDraftBodyRangesColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "messageDraftBodyRanges", columnType: .blob, isOptional: true) }
|
||||
static var mentionNotificationModeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "mentionNotificationMode", columnType: .int) }
|
||||
static var mutedUntilTimestampObsoleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "mutedUntilTimestampObsolete", columnType: .int64) }
|
||||
static var allowsRepliesColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "allowsReplies", columnType: .int, isOptional: true) }
|
||||
static var lastSentStoryTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "lastSentStoryTimestamp", columnType: .int64, isOptional: true) }
|
||||
static var nameColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "name", columnType: .unicodeString, isOptional: true) }
|
||||
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(
|
||||
tableName: "model_TSThread",
|
||||
columns: [
|
||||
idColumn,
|
||||
recordTypeColumn,
|
||||
uniqueIdColumn,
|
||||
conversationColorNameObsoleteColumn,
|
||||
creationDateColumn,
|
||||
isArchivedObsoleteColumn,
|
||||
lastInteractionRowIdColumn,
|
||||
messageDraftColumn,
|
||||
mutedUntilDateObsoleteColumn,
|
||||
shouldThreadBeVisibleColumn,
|
||||
contactPhoneNumberColumn,
|
||||
contactUUIDColumn,
|
||||
groupModelColumn,
|
||||
hasDismissedOffersColumn,
|
||||
isMarkedUnreadObsoleteColumn,
|
||||
lastVisibleSortIdOnScreenPercentageObsoleteColumn,
|
||||
lastVisibleSortIdObsoleteColumn,
|
||||
messageDraftBodyRangesColumn,
|
||||
mentionNotificationModeColumn,
|
||||
mutedUntilTimestampObsoleteColumn,
|
||||
allowsRepliesColumn,
|
||||
lastSentStoryTimestampColumn,
|
||||
nameColumn,
|
||||
addressesColumn,
|
||||
storyViewModeColumn,
|
||||
editTargetTimestampColumn,
|
||||
lastDraftInteractionRowIdColumn,
|
||||
lastDraftUpdateTimestampColumn,
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Save/Remove/Update
|
||||
|
||||
@objc
|
||||
public extension TSThread {
|
||||
func anyInsert(transaction: DBWriteTransaction) {
|
||||
sdsSave(saveMode: .insert, transaction: transaction)
|
||||
}
|
||||
|
||||
// Avoid this method whenever feasible.
|
||||
//
|
||||
// If the record has previously been saved, this method does an overwriting
|
||||
// update of the corresponding row, otherwise if it's a new record, this
|
||||
// method inserts a new row.
|
||||
//
|
||||
// For performance, when possible, you should explicitly specify whether
|
||||
// you are inserting or updating rather than calling this method.
|
||||
func anyUpsert(transaction: DBWriteTransaction) {
|
||||
let isInserting: Bool
|
||||
if TSThread.fetchViaCache(uniqueId: uniqueId, transaction: transaction) != nil {
|
||||
isInserting = false
|
||||
} else {
|
||||
isInserting = true
|
||||
}
|
||||
sdsSave(saveMode: isInserting ? .insert : .update, transaction: transaction)
|
||||
}
|
||||
|
||||
// This method is used by "updateWith..." methods.
|
||||
//
|
||||
// This model may be updated from many threads. We don't want to save
|
||||
// our local copy (this instance) since it may be out of date. We also
|
||||
// want to avoid re-saving a model that has been deleted. Therefore, we
|
||||
// use "updateWith..." methods to:
|
||||
//
|
||||
// a) Update a property of this instance.
|
||||
// b) If a copy of this model exists in the database, load an up-to-date copy,
|
||||
// and update and save that copy.
|
||||
// b) If a copy of this model _DOES NOT_ exist in the database, do _NOT_ save
|
||||
// this local instance.
|
||||
//
|
||||
// After "updateWith...":
|
||||
//
|
||||
// a) Any copy of this model in the database will have been updated.
|
||||
// b) The local property on this instance will always have been updated.
|
||||
// c) Other properties on this instance may be out of date.
|
||||
//
|
||||
// All mutable properties of this class have been made read-only to
|
||||
// prevent accidentally modifying them directly.
|
||||
//
|
||||
// This isn't a perfect arrangement, but in practice this will prevent
|
||||
// data loss and will resolve all known issues.
|
||||
func anyUpdate(transaction: DBWriteTransaction, block: (TSThread) -> Void) {
|
||||
|
||||
block(self)
|
||||
|
||||
// If it's not saved, we don't expect to find it in the database, and we
|
||||
// won't save any changes we make back into the database.
|
||||
guard shouldBeSaved else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let dbCopy = type(of: self).anyFetch(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't apply the block twice to the same instance.
|
||||
// It's at least unnecessary and actually wrong for some blocks.
|
||||
// e.g. `block: { $0 in $0.someField++ }`
|
||||
if dbCopy !== self {
|
||||
block(dbCopy)
|
||||
}
|
||||
|
||||
dbCopy.sdsSave(saveMode: .update, transaction: transaction)
|
||||
}
|
||||
|
||||
// This method is an alternative to `anyUpdate(transaction:block:)` methods.
|
||||
//
|
||||
// We should generally use `anyUpdate` to ensure we're not unintentionally
|
||||
// clobbering other columns in the database when another concurrent update
|
||||
// has occurred.
|
||||
//
|
||||
// There are cases when this doesn't make sense, e.g. when we know we've
|
||||
// just loaded the model in the same transaction. In those cases it is
|
||||
// safe and faster to do a "overwriting" update
|
||||
func anyOverwritingUpdate(transaction: DBWriteTransaction) {
|
||||
sdsSave(saveMode: .update, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TSThreadCursor
|
||||
|
||||
@objc
|
||||
public class TSThreadCursor: NSObject, SDSCursor {
|
||||
private let transaction: DBReadTransaction
|
||||
private let cursor: RecordCursor<ThreadRecord>
|
||||
|
||||
init(transaction: DBReadTransaction, cursor: RecordCursor<ThreadRecord>) {
|
||||
self.transaction = transaction
|
||||
self.cursor = cursor
|
||||
}
|
||||
|
||||
public func next() throws -> TSThread? {
|
||||
guard let record = try cursor.next() else {
|
||||
return nil
|
||||
}
|
||||
let value = try TSThread.fromRecord(record)
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didReadThread(value, transaction: transaction)
|
||||
return value
|
||||
}
|
||||
|
||||
public func all() throws -> [TSThread] {
|
||||
var result = [TSThread]()
|
||||
while true {
|
||||
guard let model = try next() else {
|
||||
break
|
||||
}
|
||||
result.append(model)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Obj-C Fetch
|
||||
|
||||
@objc
|
||||
public extension TSThread {
|
||||
@nonobjc
|
||||
class func grdbFetchCursor(transaction: DBReadTransaction) -> TSThreadCursor {
|
||||
let database = transaction.database
|
||||
return failIfThrows {
|
||||
let cursor = try ThreadRecord.fetchCursor(database)
|
||||
return TSThreadCursor(transaction: transaction, cursor: cursor)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetches a single model by "unique id".
|
||||
class func fetchViaCache(uniqueId: String, transaction: DBReadTransaction) -> TSThread? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
if let cachedCopy = SSKEnvironment.shared.modelReadCachesRef.threadReadCache.getThread(uniqueId: uniqueId, transaction: transaction) {
|
||||
return cachedCopy
|
||||
}
|
||||
|
||||
return anyFetch(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
// Fetches a single model by "unique id".
|
||||
class func anyFetch(uniqueId: String, transaction: DBReadTransaction) -> TSThread? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
let sql = "SELECT * FROM \(ThreadRecord.databaseTableName) WHERE \(threadColumn: .uniqueId) = ?"
|
||||
return grdbFetchOne(sql: sql, arguments: [uniqueId], transaction: transaction)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
block: (TSThread, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
anyEnumerate(transaction: transaction, batched: false, block: block)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
batched: Bool = false,
|
||||
block: (TSThread, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
let batchSize = batched ? Batching.kDefaultBatchSize : 0
|
||||
anyEnumerate(transaction: transaction, batchSize: batchSize, block: block)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
//
|
||||
// If batchSize > 0, the enumeration is performed in autoreleased batches.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
batchSize: UInt,
|
||||
block: (TSThread, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
let cursor = TSThread.grdbFetchCursor(transaction: transaction)
|
||||
Batching.loop(batchSize: batchSize,
|
||||
loopBlock: { stop in
|
||||
do {
|
||||
guard let value = try cursor.next() else {
|
||||
stop.pointee = true
|
||||
return
|
||||
}
|
||||
block(value, stop)
|
||||
} catch let error {
|
||||
owsFailDebug("Couldn't fetch model: \(error)")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Does not order the results.
|
||||
class func anyFetchAll(transaction: DBReadTransaction) -> [TSThread] {
|
||||
var result = [TSThread]()
|
||||
anyEnumerate(transaction: transaction) { (model, _) in
|
||||
result.append(model)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class func anyCount(transaction: DBReadTransaction) -> UInt {
|
||||
return ThreadRecord.ows_fetchCount(transaction.database)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Swift Fetch
|
||||
|
||||
public extension TSThread {
|
||||
class func grdbFetchCursor(sql: String,
|
||||
arguments: StatementArguments = StatementArguments(),
|
||||
transaction: DBReadTransaction) -> TSThreadCursor {
|
||||
return failIfThrows {
|
||||
let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
|
||||
let cursor = try ThreadRecord.fetchCursor(transaction.database, sqlRequest)
|
||||
return TSThreadCursor(transaction: transaction, cursor: cursor)
|
||||
}
|
||||
}
|
||||
|
||||
class func grdbFetchOne(sql: String,
|
||||
arguments: StatementArguments = StatementArguments(),
|
||||
transaction: DBReadTransaction) -> TSThread? {
|
||||
assert(!sql.isEmpty)
|
||||
|
||||
do {
|
||||
let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
|
||||
guard let record = try ThreadRecord.fetchOne(transaction.database, sqlRequest) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let value = try TSThread.fromRecord(record)
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didReadThread(value, transaction: transaction)
|
||||
return value
|
||||
} catch {
|
||||
owsFailDebug("error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSSerializer
|
||||
|
||||
// The SDSSerializer protocol specifies how to insert and update the
|
||||
// row that corresponds to this model.
|
||||
class TSThreadSerializer: SDSSerializer {
|
||||
|
||||
private let model: TSThread
|
||||
public init(model: TSThread) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
func asRecord() -> SDSRecord {
|
||||
let id: Int64? = model.grdbId?.int64Value
|
||||
|
||||
let recordType: SDSRecordType = .thread
|
||||
let uniqueId: String = model.uniqueId
|
||||
|
||||
// Properties
|
||||
let conversationColorName: String = model.conversationColorNameObsolete
|
||||
let creationDate: Double? = archiveOptionalDate(model.creationDate)
|
||||
let isArchived: Bool = model.isArchivedObsolete
|
||||
let lastInteractionRowId: UInt64 = model.lastInteractionRowId
|
||||
let messageDraft: String? = model.messageDraft
|
||||
let mutedUntilDate: Double? = archiveOptionalDate(model.mutedUntilDateObsolete)
|
||||
let shouldThreadBeVisible: Bool = model.shouldThreadBeVisible
|
||||
let contactPhoneNumber: String? = nil
|
||||
let contactUUID: String? = nil
|
||||
let groupModel: Data? = nil
|
||||
let hasDismissedOffers: Bool? = nil
|
||||
let isMarkedUnread: Bool = model.isMarkedUnreadObsolete
|
||||
let lastVisibleSortIdOnScreenPercentage: Double = model.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let lastVisibleSortId: UInt64 = model.lastVisibleSortIdObsolete
|
||||
let messageDraftBodyRanges: Data? = optionalArchive(model.messageDraftBodyRanges)
|
||||
let mentionNotificationMode: UInt = model.mentionNotificationMode.rawValue
|
||||
let mutedUntilTimestamp: UInt64 = model.mutedUntilTimestampObsolete
|
||||
let allowsReplies: Bool? = nil
|
||||
let lastSentStoryTimestamp: UInt64? = archiveOptionalNSNumber(model.lastSentStoryTimestamp, conversion: { $0.uint64Value })
|
||||
let name: String? = nil
|
||||
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, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/BaseModel.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class DBReadTransaction;
|
||||
@class DBWriteTransaction;
|
||||
@class MessageBody;
|
||||
@class MessageBodyRanges;
|
||||
@class SignalServiceAddress;
|
||||
@class TSInteraction;
|
||||
@class TSInvalidIdentityKeyReceivingErrorMessage;
|
||||
@class ThreadReplyInfoObjC;
|
||||
|
||||
typedef NS_CLOSED_ENUM(NSUInteger, TSThreadMentionNotificationMode) {
|
||||
TSThreadMentionNotificationMode_Default = 0,
|
||||
TSThreadMentionNotificationMode_Always,
|
||||
TSThreadMentionNotificationMode_Never
|
||||
};
|
||||
|
||||
typedef NS_CLOSED_ENUM(NSUInteger, TSThreadStoryViewMode) {
|
||||
TSThreadStoryViewMode_Default = 0,
|
||||
TSThreadStoryViewMode_Explicit,
|
||||
TSThreadStoryViewMode_BlockList,
|
||||
TSThreadStoryViewMode_Disabled
|
||||
};
|
||||
|
||||
/**
|
||||
* TSThread is the superclass of TSContactThread, TSGroupThread, and TSPrivateStoryThread
|
||||
*/
|
||||
@interface TSThread : BaseModel
|
||||
|
||||
@property (nonatomic) TSThreadStoryViewMode storyViewMode;
|
||||
@property (nonatomic, nullable) NSNumber *lastSentStoryTimestamp;
|
||||
|
||||
@property (nonatomic) BOOL shouldThreadBeVisible;
|
||||
@property (nonatomic, readonly, nullable) NSDate *creationDate;
|
||||
@property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting DEPRECATED_MSG_ATTRIBUTE(
|
||||
"this property is only to be used in the sortId migration");
|
||||
@property (nonatomic, readonly) BOOL isArchivedObsolete;
|
||||
@property (nonatomic, readonly) BOOL isMarkedUnreadObsolete;
|
||||
|
||||
// This maintains the row Id that was at the bottom of the conversation
|
||||
// the last time the user viewed this thread so we can restore their
|
||||
// scroll position.
|
||||
//
|
||||
// If the referenced message is deleted, this value is
|
||||
// updated to point to the previous message in the conversation.
|
||||
//
|
||||
// If a new message is inserted into the conversation, this value
|
||||
// is cleared. We only restore this state if there are no unread messages.
|
||||
@property (nonatomic, readonly) uint64_t lastVisibleSortIdObsolete;
|
||||
@property (nonatomic, readonly) double lastVisibleSortIdOnScreenPercentageObsolete;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *messageDraft;
|
||||
@property (nonatomic, nullable) MessageBodyRanges *messageDraftBodyRanges;
|
||||
|
||||
// zero if thread has never had an interaction.
|
||||
// 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;
|
||||
@property (nonatomic, readonly, nullable) NSDate *mutedUntilDateObsolete;
|
||||
|
||||
@property (nonatomic) TSThreadMentionNotificationMode mentionNotificationMode;
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId uniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)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
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
@property (nonatomic, readonly) NSString *conversationColorNameObsolete;
|
||||
|
||||
/**
|
||||
* @returns recipientId for each recipient in the thread
|
||||
*/
|
||||
|
||||
@property (nonatomic, readonly) NSArray<SignalServiceAddress *> *recipientAddressesWithSneakyTransaction;
|
||||
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithTransaction:(DBReadTransaction *)transaction;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isNoteToSelf;
|
||||
|
||||
#pragma mark Interactions
|
||||
|
||||
- (BOOL)hasSafetyNumbers;
|
||||
|
||||
- (nullable TSInteraction *)lastInteractionForInboxForChatListSorting:(BOOL)isForSorting
|
||||
transaction:(DBReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(lastInteractionForInbox(forChatListSorting:transaction:));
|
||||
|
||||
- (nullable TSInteraction *)firstInteractionAtOrAroundSortId:(uint64_t)sortId
|
||||
transaction:(DBReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(firstInteraction(atOrAroundSortId:transaction:));
|
||||
|
||||
#pragma mark - Merging
|
||||
|
||||
- (void)mergeFrom:(TSThread *)otherThread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,304 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "TSThread.h"
|
||||
#import "OWSReadTracking.h"
|
||||
#import "TSIncomingMessage.h"
|
||||
#import "TSInfoMessage.h"
|
||||
#import "TSInteraction.h"
|
||||
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
@import Intents;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TSThread ()
|
||||
|
||||
@property (nonatomic, nullable) NSDate *creationDate;
|
||||
@property (nonatomic) BOOL isArchivedObsolete;
|
||||
@property (nonatomic) BOOL isMarkedUnreadObsolete;
|
||||
|
||||
@property (atomic) uint64_t mutedUntilTimestampObsolete;
|
||||
|
||||
@property (nonatomic, nullable) NSDate *mutedUntilDateObsolete;
|
||||
@property (nonatomic) uint64_t lastVisibleSortIdObsolete;
|
||||
@property (nonatomic) double lastVisibleSortIdOnScreenPercentageObsolete;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation TSThread
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId
|
||||
{
|
||||
self = [super initWithUniqueId:uniqueId];
|
||||
|
||||
if (self) {
|
||||
_creationDate = [NSDate date];
|
||||
_messageDraft = nil;
|
||||
_conversationColorNameObsolete = @"Obsolete";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
{
|
||||
self = [super initWithGrdbId:grdbId
|
||||
uniqueId:uniqueId];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_conversationColorNameObsolete = conversationColorNameObsolete;
|
||||
_creationDate = creationDate;
|
||||
_editTargetTimestamp = editTargetTimestamp;
|
||||
_isArchivedObsolete = isArchivedObsolete;
|
||||
_isMarkedUnreadObsolete = isMarkedUnreadObsolete;
|
||||
_lastDraftInteractionRowId = lastDraftInteractionRowId;
|
||||
_lastDraftUpdateTimestamp = lastDraftUpdateTimestamp;
|
||||
_lastInteractionRowId = lastInteractionRowId;
|
||||
_lastSentStoryTimestamp = lastSentStoryTimestamp;
|
||||
_lastVisibleSortIdObsolete = lastVisibleSortIdObsolete;
|
||||
_lastVisibleSortIdOnScreenPercentageObsolete = lastVisibleSortIdOnScreenPercentageObsolete;
|
||||
_mentionNotificationMode = mentionNotificationMode;
|
||||
_messageDraft = messageDraft;
|
||||
_messageDraftBodyRanges = messageDraftBodyRanges;
|
||||
_mutedUntilDateObsolete = mutedUntilDateObsolete;
|
||||
_mutedUntilTimestampObsolete = mutedUntilTimestampObsolete;
|
||||
_shouldThreadBeVisible = shouldThreadBeVisible;
|
||||
_storyViewMode = storyViewMode;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = [super hash];
|
||||
result ^= self.conversationColorNameObsolete.hash;
|
||||
result ^= self.creationDate.hash;
|
||||
result ^= self.editTargetTimestamp.hash;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
result ^= self.isArchivedByLegacyTimestampForSorting;
|
||||
#pragma clang diagnostic pop
|
||||
result ^= self.isArchivedObsolete;
|
||||
result ^= self.isMarkedUnreadObsolete;
|
||||
result ^= self.lastDraftInteractionRowId;
|
||||
result ^= self.lastDraftUpdateTimestamp;
|
||||
result ^= self.lastInteractionRowId;
|
||||
result ^= self.lastSentStoryTimestamp.hash;
|
||||
result ^= self.lastVisibleSortIdObsolete;
|
||||
result ^= @(self.lastVisibleSortIdOnScreenPercentageObsolete).stringValue.hash;
|
||||
result ^= self.mentionNotificationMode;
|
||||
result ^= self.messageDraft.hash;
|
||||
result ^= self.messageDraftBodyRanges.hash;
|
||||
result ^= self.mutedUntilDateObsolete.hash;
|
||||
result ^= self.mutedUntilTimestampObsolete;
|
||||
result ^= self.shouldThreadBeVisible;
|
||||
result ^= self.storyViewMode;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![super isEqual:other]) {
|
||||
return NO;
|
||||
}
|
||||
TSThread *typedOther = (TSThread *)other;
|
||||
if (![NSObject isObject:self.conversationColorNameObsolete
|
||||
equalToObject:typedOther.conversationColorNameObsolete]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.creationDate equalToObject:typedOther.creationDate]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.editTargetTimestamp equalToObject:typedOther.editTargetTimestamp]) {
|
||||
return NO;
|
||||
}
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
if (self.isArchivedByLegacyTimestampForSorting != typedOther.isArchivedByLegacyTimestampForSorting) {
|
||||
return NO;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
if (self.isArchivedObsolete != typedOther.isArchivedObsolete) {
|
||||
return NO;
|
||||
}
|
||||
if (self.isMarkedUnreadObsolete != typedOther.isMarkedUnreadObsolete) {
|
||||
return NO;
|
||||
}
|
||||
if (self.lastDraftInteractionRowId != typedOther.lastDraftInteractionRowId) {
|
||||
return NO;
|
||||
}
|
||||
if (self.lastDraftUpdateTimestamp != typedOther.lastDraftUpdateTimestamp) {
|
||||
return NO;
|
||||
}
|
||||
if (self.lastInteractionRowId != typedOther.lastInteractionRowId) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.lastSentStoryTimestamp equalToObject:typedOther.lastSentStoryTimestamp]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.lastVisibleSortIdObsolete != typedOther.lastVisibleSortIdObsolete) {
|
||||
return NO;
|
||||
}
|
||||
if (self.lastVisibleSortIdOnScreenPercentageObsolete != typedOther.lastVisibleSortIdOnScreenPercentageObsolete) {
|
||||
return NO;
|
||||
}
|
||||
if (self.mentionNotificationMode != typedOther.mentionNotificationMode) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.messageDraft equalToObject:typedOther.messageDraft]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.messageDraftBodyRanges equalToObject:typedOther.messageDraftBodyRanges]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.mutedUntilDateObsolete equalToObject:typedOther.mutedUntilDateObsolete]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.mutedUntilTimestampObsolete != typedOther.mutedUntilTimestampObsolete) {
|
||||
return NO;
|
||||
}
|
||||
if (self.shouldThreadBeVisible != typedOther.shouldThreadBeVisible) {
|
||||
return NO;
|
||||
}
|
||||
if (self.storyViewMode != typedOther.storyViewMode) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)anyDidInsertWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidInsertWithTransaction:transaction];
|
||||
|
||||
[ThreadAssociatedData createFor:self.uniqueId transaction:transaction];
|
||||
|
||||
if (self.shouldThreadBeVisible && ![SSKPreferences hasSavedThreadWithTransaction:transaction]) {
|
||||
[SSKPreferences setHasSavedThread:YES transaction:transaction];
|
||||
}
|
||||
|
||||
[self _anyDidInsertWithTx:transaction];
|
||||
|
||||
[SSKEnvironment.shared.modelReadCachesRef.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidUpdateWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidUpdateWithTransaction:transaction];
|
||||
|
||||
if (self.shouldThreadBeVisible && ![SSKPreferences hasSavedThreadWithTransaction:transaction]) {
|
||||
[SSKPreferences setHasSavedThread:YES transaction:transaction];
|
||||
}
|
||||
|
||||
[SSKEnvironment.shared.modelReadCachesRef.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
|
||||
|
||||
[PinnedThreadManagerObjcBridge handleUpdatedThread:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (BOOL)isNoteToSelf
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - To be subclassed.
|
||||
|
||||
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithSneakyTransaction
|
||||
{
|
||||
__block NSArray<SignalServiceAddress *> *recipientAddresses;
|
||||
[SSKEnvironment.shared.databaseStorageRef readWithBlock:^(
|
||||
DBReadTransaction *transaction) { recipientAddresses = [self recipientAddressesWithTransaction:transaction]; }];
|
||||
return recipientAddresses;
|
||||
}
|
||||
|
||||
|
||||
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithTransaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
OWSAbstractMethod();
|
||||
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (BOOL)hasSafetyNumbers
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Interactions
|
||||
|
||||
- (nullable TSInteraction *)lastInteractionForInboxForChatListSorting:(BOOL)isForSorting
|
||||
transaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId]
|
||||
mostRecentInteractionForInboxForChatListSorting:isForSorting
|
||||
transaction:transaction];
|
||||
}
|
||||
|
||||
- (nullable TSInteraction *)firstInteractionAtOrAroundSortId:(uint64_t)sortId
|
||||
transaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
return
|
||||
[[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] firstInteractionAtOrAroundSortId:sortId
|
||||
transaction:transaction];
|
||||
}
|
||||
|
||||
#pragma mark - Merging
|
||||
|
||||
- (void)mergeFrom:(TSThread *)otherThread
|
||||
{
|
||||
self.shouldThreadBeVisible = self.shouldThreadBeVisible || otherThread.shouldThreadBeVisible;
|
||||
self.lastInteractionRowId = MAX(self.lastInteractionRowId, otherThread.lastInteractionRowId);
|
||||
|
||||
// Copy the draft if this thread doesn't have one. We always assign both
|
||||
// values if we assign one of them since they're related.
|
||||
if (self.messageDraft == nil) {
|
||||
self.messageDraft = otherThread.messageDraft;
|
||||
self.messageDraftBodyRanges = otherThread.messageDraftBodyRanges;
|
||||
self.lastDraftInteractionRowId = otherThread.lastDraftInteractionRowId;
|
||||
self.lastDraftUpdateTimestamp = otherThread.lastDraftUpdateTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -3,11 +3,367 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import GRDB
|
||||
public import GRDB
|
||||
public import LibSignalClient
|
||||
|
||||
extension TSThread {
|
||||
public static var databaseTableName: String { ThreadRecord.databaseTableName }
|
||||
public enum TSThreadMentionNotificationMode: UInt {
|
||||
case `default` = 0
|
||||
case always = 1
|
||||
case never = 2
|
||||
}
|
||||
|
||||
public enum TSThreadStoryViewMode: UInt {
|
||||
case `default` = 0
|
||||
case explicit = 1
|
||||
case blockList = 2
|
||||
case disabled = 3
|
||||
}
|
||||
|
||||
@objc
|
||||
open class TSThread: NSObject, SDSCodableModel, InheritableRecord {
|
||||
public static let databaseTableName: String = "model_TSThread"
|
||||
public class var recordType: SDSRecordType { .thread }
|
||||
|
||||
static func concreteType(forRecordType recordType: UInt) -> (any InheritableRecord.Type)? {
|
||||
switch recordType {
|
||||
case SDSRecordType.thread.rawValue: TSThread.self
|
||||
case SDSRecordType.contactThread.rawValue: TSContactThread.self
|
||||
case SDSRecordType.groupThread.rawValue: TSGroupThread.self
|
||||
case SDSRecordType.privateStoryThread.rawValue: TSPrivateStoryThread.self
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
public var id: Int64?
|
||||
public var sqliteRowId: Int64? { self.id }
|
||||
|
||||
@objc
|
||||
public let uniqueId: String
|
||||
|
||||
public let conversationColorNameObsolete: String
|
||||
public let creationDate: Date?
|
||||
public let isArchivedObsolete: Bool
|
||||
// zero if thread has never had an interaction.
|
||||
// The corresponding interaction may have been deleted.
|
||||
public internal(set) var lastInteractionRowId: UInt64
|
||||
public internal(set) var messageDraft: String?
|
||||
public let mutedUntilDateObsolete: Date?
|
||||
public internal(set) var shouldThreadBeVisible: Bool
|
||||
public let isMarkedUnreadObsolete: Bool
|
||||
public let lastVisibleSortIdOnScreenPercentageObsolete: Double
|
||||
// This maintains the row Id that was at the bottom of the conversation
|
||||
// the last time the user viewed this thread so we can restore their
|
||||
// scroll position.
|
||||
//
|
||||
// If the referenced message is deleted, this value is
|
||||
// updated to point to the previous message in the conversation.
|
||||
//
|
||||
// If a new message is inserted into the conversation, this value
|
||||
// is cleared. We only restore this state if there are no unread messages.
|
||||
public let lastVisibleSortIdObsolete: UInt64
|
||||
public private(set) var messageDraftBodyRanges: MessageBodyRanges?
|
||||
public private(set) var mentionNotificationMode: TSThreadMentionNotificationMode
|
||||
public let mutedUntilTimestampObsolete: UInt64
|
||||
public private(set) var lastSentStoryTimestamp: UInt64?
|
||||
public internal(set) var storyViewMode: TSThreadStoryViewMode
|
||||
public private(set) var editTargetTimestamp: UInt64?
|
||||
// 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.
|
||||
public internal(set) var lastDraftInteractionRowId: UInt64
|
||||
public internal(set) var lastDraftUpdateTimestamp: UInt64
|
||||
|
||||
// DEPRECATED_MSG_ATTRIBUTE("this property is only to be used in the sortId migration");
|
||||
let isArchivedByLegacyTimestampForSorting: Bool = false
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression {
|
||||
case id
|
||||
case recordType
|
||||
case uniqueId
|
||||
|
||||
case conversationColorNameObsolete = "conversationColorName"
|
||||
case creationDate
|
||||
case editTargetTimestamp
|
||||
case isArchivedObsolete = "isArchived"
|
||||
case isMarkedUnreadObsolete = "isMarkedUnread"
|
||||
case lastDraftInteractionRowId
|
||||
case lastDraftUpdateTimestamp
|
||||
case lastInteractionRowId
|
||||
case lastSentStoryTimestamp
|
||||
case lastVisibleSortIdObsolete = "lastVisibleSortId"
|
||||
case lastVisibleSortIdOnScreenPercentageObsolete = "lastVisibleSortIdOnScreenPercentage"
|
||||
case mentionNotificationMode
|
||||
case messageDraft
|
||||
case messageDraftBodyRanges
|
||||
case mutedUntilDateObsolete = "mutedUntilDate"
|
||||
case mutedUntilTimestampObsolete = "mutedUntilTimestamp"
|
||||
case shouldThreadBeVisible
|
||||
case storyViewMode
|
||||
}
|
||||
|
||||
public required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.id = try container.decodeIfPresent(Int64.self, forKey: .id)
|
||||
self.uniqueId = try container.decode(String.self, forKey: .uniqueId)
|
||||
self.conversationColorNameObsolete = try container.decode(String.self, forKey: .conversationColorNameObsolete)
|
||||
self.creationDate = try container.decodeIfPresent(TimeInterval.self, forKey: .creationDate).map(Date.init(timeIntervalSince1970:))
|
||||
self.editTargetTimestamp = try container.decodeIfPresent(UInt64.self, forKey: .editTargetTimestamp)
|
||||
self.isArchivedObsolete = try container.decode(Bool.self, forKey: .isArchivedObsolete)
|
||||
self.isMarkedUnreadObsolete = try container.decode(Bool.self, forKey: .isMarkedUnreadObsolete)
|
||||
self.lastDraftInteractionRowId = try container.decode(UInt64.self, forKey: .lastDraftInteractionRowId)
|
||||
self.lastDraftUpdateTimestamp = try container.decode(UInt64.self, forKey: .lastDraftUpdateTimestamp)
|
||||
self.lastInteractionRowId = try container.decode(UInt64.self, forKey: .lastInteractionRowId)
|
||||
self.lastSentStoryTimestamp = try container.decodeIfPresent(UInt64.self, forKey: .lastSentStoryTimestamp)
|
||||
self.lastVisibleSortIdObsolete = try container.decode(UInt64.self, forKey: .lastVisibleSortIdObsolete)
|
||||
self.lastVisibleSortIdOnScreenPercentageObsolete = try container.decode(Double.self, forKey: .lastVisibleSortIdOnScreenPercentageObsolete)
|
||||
self.mentionNotificationMode = TSThreadMentionNotificationMode(rawValue: try container.decode(UInt.self, forKey: .mentionNotificationMode)) ?? .default
|
||||
self.messageDraft = try container.decodeIfPresent(String.self, forKey: .messageDraft)
|
||||
self.messageDraftBodyRanges = try container.decodeIfPresent(Data.self, forKey: .messageDraftBodyRanges).map({ try LegacySDSSerializer().deserializeLegacySDSData($0, ofClass: MessageBodyRanges.self) })
|
||||
self.mutedUntilDateObsolete = try container.decodeIfPresent(TimeInterval.self, forKey: .mutedUntilDateObsolete).map(Date.init(timeIntervalSince1970:))
|
||||
self.mutedUntilTimestampObsolete = try container.decode(UInt64.self, forKey: .mutedUntilTimestampObsolete)
|
||||
self.shouldThreadBeVisible = try container.decode(Bool.self, forKey: .shouldThreadBeVisible)
|
||||
self.storyViewMode = TSThreadStoryViewMode(rawValue: try container.decode(UInt.self, forKey: .storyViewMode)) ?? .default
|
||||
}
|
||||
|
||||
public func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.id, forKey: .id)
|
||||
try container.encode(Self.recordType.rawValue, forKey: .recordType)
|
||||
try container.encode(self.uniqueId, forKey: .uniqueId)
|
||||
try container.encode(self.conversationColorNameObsolete, forKey: .conversationColorNameObsolete)
|
||||
try container.encode(self.creationDate?.timeIntervalSince1970, forKey: .creationDate)
|
||||
try container.encode(self.editTargetTimestamp, forKey: .editTargetTimestamp)
|
||||
try container.encode(self.isArchivedObsolete, forKey: .isArchivedObsolete)
|
||||
try container.encode(self.lastDraftInteractionRowId, forKey: .lastDraftInteractionRowId)
|
||||
try container.encode(self.lastDraftUpdateTimestamp, forKey: .lastDraftUpdateTimestamp)
|
||||
try container.encode(self.lastInteractionRowId, forKey: .lastInteractionRowId)
|
||||
try container.encode(self.lastSentStoryTimestamp, forKey: .lastSentStoryTimestamp)
|
||||
try container.encode(self.lastVisibleSortIdObsolete, forKey: .lastVisibleSortIdObsolete)
|
||||
try container.encode(self.lastVisibleSortIdOnScreenPercentageObsolete, forKey: .lastVisibleSortIdOnScreenPercentageObsolete)
|
||||
try container.encode(self.mentionNotificationMode.rawValue, forKey: .mentionNotificationMode)
|
||||
try container.encode(self.messageDraft, forKey: .messageDraft)
|
||||
let messageDraftBodyRangesData = self.messageDraftBodyRanges.map(LegacySDSSerializer().serializeAsLegacySDSData(_:))
|
||||
try container.encode(messageDraftBodyRangesData, forKey: .messageDraftBodyRanges)
|
||||
try container.encode(self.mutedUntilDateObsolete, forKey: .mutedUntilDateObsolete)
|
||||
try container.encode(self.mutedUntilTimestampObsolete, forKey: .mutedUntilTimestampObsolete)
|
||||
try container.encode(self.shouldThreadBeVisible, forKey: .shouldThreadBeVisible)
|
||||
try container.encode(self.storyViewMode.rawValue, forKey: .storyViewMode)
|
||||
}
|
||||
|
||||
init(
|
||||
id: Int64?,
|
||||
uniqueId: String,
|
||||
conversationColorNameObsolete: String,
|
||||
creationDate: Date?,
|
||||
editTargetTimestamp: UInt64?,
|
||||
isArchivedObsolete: Bool,
|
||||
isMarkedUnreadObsolete: Bool,
|
||||
lastDraftInteractionRowId: UInt64,
|
||||
lastDraftUpdateTimestamp: UInt64,
|
||||
lastInteractionRowId: UInt64,
|
||||
lastSentStoryTimestamp: UInt64?,
|
||||
lastVisibleSortIdObsolete: UInt64,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: Double,
|
||||
mentionNotificationMode: TSThreadMentionNotificationMode,
|
||||
messageDraft: String?,
|
||||
messageDraftBodyRanges: MessageBodyRanges?,
|
||||
mutedUntilDateObsolete: Date?,
|
||||
mutedUntilTimestampObsolete: UInt64,
|
||||
shouldThreadBeVisible: Bool,
|
||||
storyViewMode: TSThreadStoryViewMode,
|
||||
) {
|
||||
self.id = id
|
||||
self.uniqueId = uniqueId
|
||||
self.conversationColorNameObsolete = conversationColorNameObsolete
|
||||
self.creationDate = creationDate
|
||||
self.editTargetTimestamp = editTargetTimestamp
|
||||
self.isArchivedObsolete = isArchivedObsolete
|
||||
self.isMarkedUnreadObsolete = isMarkedUnreadObsolete
|
||||
self.lastDraftInteractionRowId = lastDraftInteractionRowId
|
||||
self.lastDraftUpdateTimestamp = lastDraftUpdateTimestamp
|
||||
self.lastInteractionRowId = lastInteractionRowId
|
||||
self.lastSentStoryTimestamp = lastSentStoryTimestamp
|
||||
self.lastVisibleSortIdObsolete = lastVisibleSortIdObsolete
|
||||
self.lastVisibleSortIdOnScreenPercentageObsolete = lastVisibleSortIdOnScreenPercentageObsolete
|
||||
self.mentionNotificationMode = mentionNotificationMode
|
||||
self.messageDraft = messageDraft
|
||||
self.messageDraftBodyRanges = messageDraftBodyRanges
|
||||
self.mutedUntilDateObsolete = mutedUntilDateObsolete
|
||||
self.mutedUntilTimestampObsolete = mutedUntilTimestampObsolete
|
||||
self.shouldThreadBeVisible = shouldThreadBeVisible
|
||||
self.storyViewMode = storyViewMode
|
||||
}
|
||||
|
||||
init(uniqueId: String) {
|
||||
self.conversationColorNameObsolete = "Obsolete"
|
||||
self.isArchivedObsolete = false
|
||||
self.isMarkedUnreadObsolete = false
|
||||
self.lastDraftInteractionRowId = 0
|
||||
self.lastDraftUpdateTimestamp = 0
|
||||
self.lastInteractionRowId = 0
|
||||
self.lastVisibleSortIdObsolete = 0
|
||||
self.lastVisibleSortIdOnScreenPercentageObsolete = 0
|
||||
self.mentionNotificationMode = .default
|
||||
self.messageDraft = nil
|
||||
self.mutedUntilDateObsolete = nil
|
||||
self.mutedUntilTimestampObsolete = 0
|
||||
self.shouldThreadBeVisible = false
|
||||
self.storyViewMode = .default
|
||||
|
||||
self.uniqueId = uniqueId
|
||||
self.creationDate = Date()
|
||||
}
|
||||
|
||||
func deepCopy() -> TSThread {
|
||||
return TSThread(
|
||||
id: self.id,
|
||||
uniqueId: self.uniqueId,
|
||||
conversationColorNameObsolete: self.conversationColorNameObsolete,
|
||||
creationDate: self.creationDate,
|
||||
editTargetTimestamp: self.editTargetTimestamp,
|
||||
isArchivedObsolete: self.isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: self.isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: self.lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: self.lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: self.lastInteractionRowId,
|
||||
lastSentStoryTimestamp: self.lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: self.lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: self.lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: self.mentionNotificationMode,
|
||||
messageDraft: self.messageDraft,
|
||||
messageDraftBodyRanges: self.messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: self.mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: self.mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: self.shouldThreadBeVisible,
|
||||
storyViewMode: self.storyViewMode,
|
||||
)
|
||||
}
|
||||
|
||||
override public var hash: Int {
|
||||
var hasher = Hasher()
|
||||
hasher.combine(self.id)
|
||||
hasher.combine(self.uniqueId)
|
||||
hasher.combine(self.conversationColorNameObsolete)
|
||||
hasher.combine(self.creationDate)
|
||||
hasher.combine(self.editTargetTimestamp)
|
||||
hasher.combine(self.isArchivedByLegacyTimestampForSorting)
|
||||
hasher.combine(self.isArchivedObsolete)
|
||||
hasher.combine(self.isMarkedUnreadObsolete)
|
||||
hasher.combine(self.lastDraftInteractionRowId)
|
||||
hasher.combine(self.lastDraftUpdateTimestamp)
|
||||
hasher.combine(self.lastInteractionRowId)
|
||||
hasher.combine(self.lastSentStoryTimestamp)
|
||||
hasher.combine(self.lastVisibleSortIdObsolete)
|
||||
hasher.combine(self.lastVisibleSortIdOnScreenPercentageObsolete)
|
||||
hasher.combine(self.mentionNotificationMode)
|
||||
hasher.combine(self.messageDraft)
|
||||
hasher.combine(self.messageDraftBodyRanges)
|
||||
hasher.combine(self.mutedUntilDateObsolete)
|
||||
hasher.combine(self.mutedUntilTimestampObsolete)
|
||||
hasher.combine(self.shouldThreadBeVisible)
|
||||
hasher.combine(self.storyViewMode)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? Self else { return false }
|
||||
guard self.id == object.id else { return false }
|
||||
guard self.uniqueId == object.uniqueId else { return false }
|
||||
guard self.conversationColorNameObsolete == object.conversationColorNameObsolete else { return false }
|
||||
guard self.creationDate == object.creationDate else { return false }
|
||||
guard self.editTargetTimestamp == object.editTargetTimestamp else { return false }
|
||||
guard self.isArchivedByLegacyTimestampForSorting == object.isArchivedByLegacyTimestampForSorting else { return false }
|
||||
guard self.isArchivedObsolete == object.isArchivedObsolete else { return false }
|
||||
guard self.isMarkedUnreadObsolete == object.isMarkedUnreadObsolete else { return false }
|
||||
guard self.lastDraftInteractionRowId == object.lastDraftInteractionRowId else { return false }
|
||||
guard self.lastDraftUpdateTimestamp == object.lastDraftUpdateTimestamp else { return false }
|
||||
guard self.lastInteractionRowId == object.lastInteractionRowId else { return false }
|
||||
guard self.lastSentStoryTimestamp == object.lastSentStoryTimestamp else { return false }
|
||||
guard self.lastVisibleSortIdObsolete == object.lastVisibleSortIdObsolete else { return false }
|
||||
guard self.lastVisibleSortIdOnScreenPercentageObsolete == object.lastVisibleSortIdOnScreenPercentageObsolete else { return false }
|
||||
guard self.mentionNotificationMode == object.mentionNotificationMode else { return false }
|
||||
guard self.messageDraft == object.messageDraft else { return false }
|
||||
guard self.messageDraftBodyRanges == object.messageDraftBodyRanges else { return false }
|
||||
guard self.mutedUntilDateObsolete == object.mutedUntilDateObsolete else { return false }
|
||||
guard self.mutedUntilTimestampObsolete == object.mutedUntilTimestampObsolete else { return false }
|
||||
guard self.shouldThreadBeVisible == object.shouldThreadBeVisible else { return false }
|
||||
guard self.storyViewMode == object.storyViewMode else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public func anyDidFetchOne(transaction: DBReadTransaction) {
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didReadThread(self, transaction: transaction)
|
||||
}
|
||||
|
||||
public func anyDidEnumerateOne(transaction: DBReadTransaction) {
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didReadThread(self, transaction: transaction)
|
||||
}
|
||||
|
||||
open func anyWillInsert(transaction: DBWriteTransaction) {
|
||||
}
|
||||
|
||||
public func anyDidInsert(transaction: DBWriteTransaction) {
|
||||
ThreadAssociatedData.create(for: self.uniqueId, transaction: transaction)
|
||||
|
||||
if self.shouldThreadBeVisible, !SSKPreferences.hasSavedThread(transaction: transaction) {
|
||||
SSKPreferences.setHasSavedThread(true, transaction: transaction)
|
||||
}
|
||||
|
||||
_anyDidInsert(tx: transaction)
|
||||
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didInsertOrUpdate(thread: self, transaction: transaction)
|
||||
}
|
||||
|
||||
public func anyWillUpdate(transaction: DBWriteTransaction) {
|
||||
}
|
||||
|
||||
public func anyDidUpdate(transaction: DBWriteTransaction) {
|
||||
if self.shouldThreadBeVisible, !SSKPreferences.hasSavedThread(transaction: transaction) {
|
||||
SSKPreferences.setHasSavedThread(true, transaction: transaction)
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.modelReadCachesRef.threadReadCache.didInsertOrUpdate(thread: self, transaction: transaction)
|
||||
|
||||
DependenciesBridge.shared.pinnedThreadManager.handleUpdatedThread(self, tx: transaction)
|
||||
}
|
||||
|
||||
public var isNoteToSelf: Bool { false }
|
||||
|
||||
public final var recipientAddressesWithSneakyTransaction: [SignalServiceAddress] {
|
||||
let databaseStorage = SSKEnvironment.shared.databaseStorageRef
|
||||
return databaseStorage.read { tx in self.recipientAddresses(with: tx) }
|
||||
}
|
||||
|
||||
@objc
|
||||
public func recipientAddresses(with tx: DBReadTransaction) -> [SignalServiceAddress] {
|
||||
owsFail("abstract method")
|
||||
}
|
||||
|
||||
public func hasSafetyNumbers() -> Bool { false }
|
||||
|
||||
public func lastInteractionForInbox(forChatListSorting isForSorting: Bool, transaction tx: DBReadTransaction) -> TSInteraction? {
|
||||
return InteractionFinder(threadUniqueId: self.uniqueId).mostRecentInteractionForInbox(forChatListSorting: isForSorting, transaction: tx)
|
||||
}
|
||||
|
||||
public func firstInteraction(atOrAroundSortId sortId: UInt64, transaction tx: DBReadTransaction) -> TSInteraction? {
|
||||
return InteractionFinder(threadUniqueId: self.uniqueId).firstInteraction(atOrAroundSortId: sortId, transaction: tx)
|
||||
}
|
||||
|
||||
func merge(from otherThread: TSThread) {
|
||||
self.shouldThreadBeVisible = self.shouldThreadBeVisible || otherThread.shouldThreadBeVisible
|
||||
self.lastInteractionRowId = max(self.lastInteractionRowId, otherThread.lastInteractionRowId)
|
||||
|
||||
// Copy the draft if this thread doesn't have one. We always assign both
|
||||
// values if we assign one of them since they're related.
|
||||
if self.messageDraft == nil {
|
||||
self.messageDraft = otherThread.messageDraft
|
||||
self.messageDraftBodyRanges = otherThread.messageDraftBodyRanges
|
||||
self.lastDraftInteractionRowId = otherThread.lastDraftInteractionRowId
|
||||
self.lastDraftUpdateTimestamp = otherThread.lastDraftUpdateTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
public typealias RowId = Int64
|
||||
|
||||
@ -15,23 +371,21 @@ extension TSThread {
|
||||
return (self as? TSGroupThread)?.groupId.toHex() ?? self.uniqueId
|
||||
}
|
||||
|
||||
// [SDS] TODO: Replace with SDSCodableModel.
|
||||
static func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
sql: String,
|
||||
arguments: StatementArguments,
|
||||
block: (TSThread, UnsafeMutablePointer<ObjCBool>) -> Void,
|
||||
) {
|
||||
let cursor = TSThread.grdbFetchCursor(sql: sql, arguments: arguments, transaction: transaction)
|
||||
failIfThrows {
|
||||
var stop: ObjCBool = false
|
||||
while let thread = try cursor.next() {
|
||||
block(thread, &stop)
|
||||
if stop.boolValue {
|
||||
break
|
||||
}
|
||||
}
|
||||
@objc
|
||||
public class func fetchViaCacheObjC(uniqueId: String, transaction: DBReadTransaction) -> TSThread? {
|
||||
return fetchViaCache(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
public class func fetchViaCache(uniqueId: String, transaction: DBReadTransaction) -> Self? {
|
||||
let cache = SSKEnvironment.shared.modelReadCachesRef.threadReadCache
|
||||
guard let thread = cache.getThread(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return nil
|
||||
}
|
||||
guard let typedThread = thread as? Self else {
|
||||
owsFailDebug("object has type \(type(of: thread)), not \(Self.self)")
|
||||
return nil
|
||||
}
|
||||
return typedThread
|
||||
}
|
||||
|
||||
// MARK: - updateWith...
|
||||
@ -47,7 +401,7 @@ extension TSThread {
|
||||
anyUpdate(transaction: tx) { thread in
|
||||
thread.messageDraft = draftMessageBody?.text
|
||||
thread.messageDraftBodyRanges = draftMessageBody?.ranges
|
||||
thread.editTargetTimestamp = editTargetTimestamp.map { NSNumber(value: $0) }
|
||||
thread.editTargetTimestamp = editTargetTimestamp
|
||||
|
||||
if draftMessageBody?.text.nilIfEmpty == nil {
|
||||
// 0 makes these values effectively irrelevant since they will
|
||||
@ -105,8 +459,8 @@ extension TSThread {
|
||||
transaction tx: DBWriteTransaction,
|
||||
) {
|
||||
anyUpdate(transaction: tx) { thread in
|
||||
if lastSentStoryTimestamp > (thread.lastSentStoryTimestamp?.uint64Value ?? 0) {
|
||||
thread.lastSentStoryTimestamp = NSNumber(value: lastSentStoryTimestamp)
|
||||
if lastSentStoryTimestamp > thread.lastSentStoryTimestamp ?? 0 {
|
||||
thread.lastSentStoryTimestamp = lastSentStoryTimestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,3 +730,15 @@ extension TSThread {
|
||||
return groupModel.access.attributes != .administrator && !groupModel.isAnnouncementsOnly
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StringInterpolation
|
||||
|
||||
public extension String.StringInterpolation {
|
||||
mutating func appendInterpolation(threadColumn column: TSThread.CodingKeys) {
|
||||
appendLiteral(column.rawValue)
|
||||
}
|
||||
|
||||
mutating func appendInterpolation(threadColumnFullyQualified column: TSThread.CodingKeys) {
|
||||
appendLiteral("\(TSThread.databaseTableName).\(column.rawValue)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,10 +65,7 @@ public class GroupV2UpdatesImpl: GroupV2Updates {
|
||||
// The "best" is the group that hasn't been refreshed in the longest time.
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
var groupInfoToRefresh: GroupInfo?
|
||||
TSGroupThread.anyEnumerate(
|
||||
transaction: transaction,
|
||||
batched: true,
|
||||
) { thread, stop in
|
||||
TSThread.anyEnumerate(transaction: transaction, batchingPreference: .batched(Batching.kDefaultBatchSize)) { thread, stop in
|
||||
guard
|
||||
let groupThread = thread as? TSGroupThread,
|
||||
let groupModel = groupThread.groupModel as? TSGroupModelV2,
|
||||
|
||||
@ -76,7 +76,7 @@ class GroupsV2ProfileKeyUpdater {
|
||||
}
|
||||
|
||||
func scheduleAllGroupsV2ForProfileKeyUpdate(transaction: DBWriteTransaction) {
|
||||
TSGroupThread.anyEnumerate(transaction: transaction) { thread, _ in
|
||||
TSThread.anyEnumerate(transaction: transaction) { thread, _ in
|
||||
guard let groupThread = thread as? TSGroupThread else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1001,7 +1001,7 @@ class AttachmentStoreTests: XCTestCase {
|
||||
|
||||
private func insertThread(tx: DBWriteTransaction) -> TSThread {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
|
||||
@ -270,7 +270,7 @@ class AttachmentDownloadStoreTests: XCTestCase {
|
||||
|
||||
private func insertAttachment(tx: DBWriteTransaction) -> Attachment.IDType {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
let interaction = TSInteraction(timestamp: 0, receivedAtTimestamp: 0, thread: thread)
|
||||
try! interaction.asRecord().insert(tx.database)
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
#import "TSIncomingMessage.h"
|
||||
#import "TSInteraction.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import "TSThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSErrorMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSIncomingMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSInteraction.h"
|
||||
#import "TSThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@ -215,7 +214,7 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
}
|
||||
|
||||
// However, it's also possible that the thread doesn't exist.
|
||||
return [TSThread fetchViaCacheWithUniqueId:self.uniqueThreadId transaction:tx];
|
||||
return [TSThread fetchViaCacheObjCWithUniqueId:self.uniqueThreadId transaction:tx];
|
||||
}
|
||||
|
||||
#pragma mark Date operations
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
|
||||
#import "TSMessage.h"
|
||||
#import "TSQuotedMessage.h"
|
||||
#import "TSThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import "TSQuotedMessage.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
@ -333,7 +331,7 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1;
|
||||
[recipientAddresses addObject:localAddress];
|
||||
} else {
|
||||
// Most messages should only be sent to the current members of the group.
|
||||
[recipientAddresses addObjectsFromArray:[thread recipientAddressesWithTransaction:transaction]];
|
||||
[recipientAddresses addObjectsFromArray:[thread recipientAddressesWith:transaction]];
|
||||
// Some messages (eg certain call messages) go to a subset of the group.
|
||||
if (explicitRecipients.count > 0) {
|
||||
NSMutableSet<SignalServiceAddress *> *explicitRecipientAddresses = [[NSMutableSet alloc] init];
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "TSInvalidIdentityKeySendingErrorMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "OWSAddToProfileWhitelistOfferMessage.h"
|
||||
#import "TSThread.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
#import "OWSRecoverableDecryptionPlaceholder.h"
|
||||
#import "TSThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -33,10 +33,8 @@ FOUNDATION_EXPORT const unsigned char SignalServiceKitVersionString[];
|
||||
#import <SignalServiceKit/OWSVerificationStateChangeMessage.h>
|
||||
#import <SignalServiceKit/SSKAccessors+SDS.h>
|
||||
#import <SignalServiceKit/TSCall.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSErrorMessage.h>
|
||||
#import <SignalServiceKit/TSGroupModel.h>
|
||||
#import <SignalServiceKit/TSGroupThread.h>
|
||||
#import <SignalServiceKit/TSIncomingMessage.h>
|
||||
#import <SignalServiceKit/TSInfoMessage.h>
|
||||
#import <SignalServiceKit/TSInteraction.h>
|
||||
@ -46,9 +44,7 @@ FOUNDATION_EXPORT const unsigned char SignalServiceKitVersionString[];
|
||||
#import <SignalServiceKit/TSMessage.h>
|
||||
#import <SignalServiceKit/TSOutgoingMessage.h>
|
||||
#import <SignalServiceKit/TSPaymentModels.h>
|
||||
#import <SignalServiceKit/TSPrivateStoryThread.h>
|
||||
#import <SignalServiceKit/TSQuotedMessage.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
#import <SignalServiceKit/TSUnreadIndicatorInteraction.h>
|
||||
#import <SignalServiceKit/TSYapDatabaseObject.h>
|
||||
#import <SignalServiceKit/Threading.h>
|
||||
|
||||
@ -5096,7 +5096,7 @@ public class GRDBSchemaMigrator {
|
||||
|
||||
migrator.registerMigration(.dataMigration_removeOversizedGroupAvatars) { transaction in
|
||||
var thrownError: Error?
|
||||
TSGroupThread.anyEnumerate(transaction: transaction) { (thread: TSThread, stop: UnsafeMutablePointer<ObjCBool>) in
|
||||
TSThread.anyEnumerate(transaction: transaction) { thread, stop in
|
||||
guard let groupThread = thread as? TSGroupThread else { return }
|
||||
guard let avatarData = groupThread.groupModel.legacyAvatarData else { return }
|
||||
guard !TSGroupModel.isValidGroupAvatarData(avatarData) else { return }
|
||||
@ -5119,7 +5119,7 @@ public class GRDBSchemaMigrator {
|
||||
migrator.registerMigration(.dataMigration_scheduleStorageServiceUpdateForMutedThreads) { transaction in
|
||||
TSThread.anyEnumerate(
|
||||
transaction: transaction,
|
||||
sql: "SELECT * FROM \(TSThread.databaseTableName) WHERE \(threadColumn: .mutedUntilTimestamp) > 0",
|
||||
sql: "SELECT * FROM \(TSThread.databaseTableName) WHERE \(threadColumn: .mutedUntilTimestampObsolete) > 0",
|
||||
arguments: [],
|
||||
) { thread, _ in
|
||||
if let thread = thread as? TSContactThread {
|
||||
|
||||
@ -20,7 +20,7 @@ public class ThreadFinder {
|
||||
/// Fetch a thread with the given SQLite row ID, if one exists.
|
||||
public func fetch(rowId: Int64, tx: DBReadTransaction) -> TSThread? {
|
||||
guard
|
||||
let thread = TSThread.grdbFetchOne(
|
||||
let thread = TSThread.anyFetch(
|
||||
sql: """
|
||||
SELECT *
|
||||
FROM \(TSThread.databaseTableName)
|
||||
@ -63,15 +63,11 @@ public class ThreadFinder {
|
||||
FROM \(TSThread.databaseTableName)
|
||||
WHERE \(threadColumn: .recordType) = \(SDSRecordType.privateStoryThread.rawValue)
|
||||
"""
|
||||
let cursor = try ThreadRecord.fetchCursor(
|
||||
let cursor = try TSPrivateStoryThread.fetchCursor(
|
||||
transaction.database,
|
||||
sql: sql,
|
||||
)
|
||||
while let threadRecord = try cursor.next() {
|
||||
guard let storyThread = (try TSThread.fromRecord(threadRecord)) as? TSPrivateStoryThread else {
|
||||
owsFailDebug("Skipping thread that's not a story.")
|
||||
continue
|
||||
}
|
||||
while let storyThread = try cursor.next() {
|
||||
guard try block(storyThread) else {
|
||||
break
|
||||
}
|
||||
@ -89,16 +85,16 @@ public class ThreadFinder {
|
||||
let sql = """
|
||||
SELECT *
|
||||
FROM \(TSThread.databaseTableName)
|
||||
WHERE \(threadColumn: .groupModel) IS NOT NULL
|
||||
WHERE \(groupThreadColumn: .groupModel) IS NOT NULL
|
||||
ORDER BY \(threadColumn: .lastInteractionRowId) DESC
|
||||
"""
|
||||
|
||||
let cursor = try ThreadRecord.fetchCursor(
|
||||
let cursor = try TSThread.fetchCursor(
|
||||
transaction.database,
|
||||
sql: sql,
|
||||
)
|
||||
while let threadRecord = try cursor.next() {
|
||||
guard let groupThread = (try TSThread.fromRecord(threadRecord)) as? TSGroupThread else {
|
||||
guard let groupThread = threadRecord as? TSGroupThread else {
|
||||
owsFailDebug("Skipping thread that's not a group.")
|
||||
continue
|
||||
}
|
||||
@ -122,15 +118,12 @@ public class ThreadFinder {
|
||||
WHERE \(threadColumn: .recordType) IS NOT ?
|
||||
"""
|
||||
|
||||
let cursor = try ThreadRecord.fetchCursor(
|
||||
let cursor = try TSThread.fetchCursor(
|
||||
transaction.database,
|
||||
sql: sql,
|
||||
arguments: [SDSRecordType.privateStoryThread.rawValue],
|
||||
)
|
||||
while
|
||||
let thread = try cursor.next().map({ try TSThread.fromRecord($0) }),
|
||||
try block(thread)
|
||||
{}
|
||||
while let thread = try cursor.next(), try block(thread) {}
|
||||
}
|
||||
|
||||
public func visibleThreadCount(
|
||||
@ -171,11 +164,11 @@ public class ThreadFinder {
|
||||
"""
|
||||
|
||||
failIfThrows {
|
||||
try ThreadRecord.fetchCursor(
|
||||
try TSThread.fetchCursor(
|
||||
transaction.database,
|
||||
sql: sql,
|
||||
).forEach { threadRecord in
|
||||
block(try TSThread.fromRecord(threadRecord))
|
||||
).forEach { thread in
|
||||
block(thread)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,11 +5,9 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SignalServiceKit/TSCall.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSIncomingMessage.h>
|
||||
#import <SignalServiceKit/TSInvalidIdentityKeyReceivingErrorMessage.h>
|
||||
#import <SignalServiceKit/TSInvalidIdentityKeySendingErrorMessage.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@ -256,7 +256,7 @@ class MediaGalleryAttachmentFinderTest: XCTestCase {
|
||||
let interaction = TSInteraction(timestamp: 0, receivedAtTimestamp: 0, thread: thread)
|
||||
|
||||
db.write { tx in
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
try! interaction.asRecord().insert(tx.database)
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ public import LibSignalClient
|
||||
/// messageFactory.create(count: 100)
|
||||
///
|
||||
public protocol Factory {
|
||||
associatedtype ObjectType: TSYapDatabaseObject
|
||||
associatedtype ObjectType
|
||||
|
||||
static func write(block: @escaping (DBWriteTransaction) -> Void)
|
||||
func write(block: @escaping (DBWriteTransaction) -> Void)
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
|
||||
// Do not manually edit it, instead run `sds_codegen.sh`.
|
||||
|
||||
// MARK: - Typed Convenience Methods
|
||||
|
||||
@objc
|
||||
public extension TSContactThread {
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
class func fetchContactThreadViaCache(
|
||||
uniqueId: String,
|
||||
transaction: DBReadTransaction
|
||||
) -> TSContactThread? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
guard let object = fetchViaCache(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return nil
|
||||
}
|
||||
guard let instance = object as? TSContactThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return nil
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
func anyUpdateContactThread(transaction: DBWriteTransaction, block: (TSContactThread) -> Void) {
|
||||
anyUpdate(transaction: transaction) { (object) in
|
||||
guard let instance = object as? TSContactThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return
|
||||
}
|
||||
block(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSSerializer
|
||||
|
||||
// The SDSSerializer protocol specifies how to insert and update the
|
||||
// row that corresponds to this model.
|
||||
class TSContactThreadSerializer: SDSSerializer {
|
||||
|
||||
private let model: TSContactThread
|
||||
public init(model: TSContactThread) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
func asRecord() -> SDSRecord {
|
||||
let id: Int64? = model.grdbId?.int64Value
|
||||
|
||||
let recordType: SDSRecordType = .contactThread
|
||||
let uniqueId: String = model.uniqueId
|
||||
|
||||
// Properties
|
||||
let conversationColorName: String = model.conversationColorNameObsolete
|
||||
let creationDate: Double? = archiveOptionalDate(model.creationDate)
|
||||
let isArchived: Bool = model.isArchivedObsolete
|
||||
let lastInteractionRowId: UInt64 = model.lastInteractionRowId
|
||||
let messageDraft: String? = model.messageDraft
|
||||
let mutedUntilDate: Double? = archiveOptionalDate(model.mutedUntilDateObsolete)
|
||||
let shouldThreadBeVisible: Bool = model.shouldThreadBeVisible
|
||||
let contactPhoneNumber: String? = model.contactPhoneNumber
|
||||
let contactUUID: String? = model.contactUUID
|
||||
let groupModel: Data? = nil
|
||||
let hasDismissedOffers: Bool? = model.hasDismissedOffers
|
||||
let isMarkedUnread: Bool = model.isMarkedUnreadObsolete
|
||||
let lastVisibleSortIdOnScreenPercentage: Double = model.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let lastVisibleSortId: UInt64 = model.lastVisibleSortIdObsolete
|
||||
let messageDraftBodyRanges: Data? = optionalArchive(model.messageDraftBodyRanges)
|
||||
let mentionNotificationMode: UInt = model.mentionNotificationMode.rawValue
|
||||
let mutedUntilTimestamp: UInt64 = model.mutedUntilTimestampObsolete
|
||||
let allowsReplies: Bool? = nil
|
||||
let lastSentStoryTimestamp: UInt64? = archiveOptionalNSNumber(model.lastSentStoryTimestamp, conversion: { $0.uint64Value })
|
||||
let name: String? = nil
|
||||
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, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class SignalServiceAddress;
|
||||
|
||||
@interface TSContactThread : TSThread
|
||||
|
||||
/// Represents the uppercase ServiceId string for this contact.
|
||||
/// - Note
|
||||
/// This property name includes `UUID` for compatibility with SDS (to match the
|
||||
/// SQLite column), but **may not contain a valid UUID string**.
|
||||
@property (nonatomic, nullable) NSString *contactUUID;
|
||||
@property (nonatomic, nullable) NSString *contactPhoneNumber;
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithContactUUID:(nullable NSString *)contactUUID
|
||||
contactPhoneNumber:(nullable NSString *)contactPhoneNumber NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode NS_UNAVAILABLE;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
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:lastDraftInteractionRowId:lastDraftUpdateTimestamp:lastInteractionRowId:lastSentStoryTimestamp:lastVisibleSortIdObsolete:lastVisibleSortIdOnScreenPercentageObsolete:mentionNotificationMode:messageDraft:messageDraftBodyRanges:mutedUntilDateObsolete:mutedUntilTimestampObsolete:shouldThreadBeVisible:storyViewMode:contactPhoneNumber:contactUUID:hasDismissedOffers:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
@property (nonatomic, readonly) SignalServiceAddress *contactAddress;
|
||||
@property (nonatomic) BOOL hasDismissedOffers; // deprecated
|
||||
|
||||
+ (nullable SignalServiceAddress *)contactAddressFromThreadId:(NSString *)threadId
|
||||
transaction:(DBReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,157 +0,0 @@
|
||||
//
|
||||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "TSContactThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation TSContactThread
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
contactPhoneNumber:(nullable NSString *)contactPhoneNumber
|
||||
contactUUID:(nullable NSString *)contactUUID
|
||||
hasDismissedOffers:(BOOL)hasDismissedOffers
|
||||
{
|
||||
self = [super initWithGrdbId:grdbId
|
||||
uniqueId:uniqueId
|
||||
conversationColorNameObsolete:conversationColorNameObsolete
|
||||
creationDate:creationDate
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:mentionNotificationMode
|
||||
messageDraft:messageDraft
|
||||
messageDraftBodyRanges:messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:shouldThreadBeVisible
|
||||
storyViewMode:storyViewMode];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_contactPhoneNumber = contactPhoneNumber;
|
||||
_contactUUID = contactUUID;
|
||||
_hasDismissedOffers = hasDismissedOffers;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = [super hash];
|
||||
result ^= self.contactPhoneNumber.hash;
|
||||
result ^= self.contactUUID.hash;
|
||||
result ^= self.hasDismissedOffers;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![super isEqual:other]) {
|
||||
return NO;
|
||||
}
|
||||
TSContactThread *typedOther = (TSContactThread *)other;
|
||||
if (![NSObject isObject:self.contactPhoneNumber equalToObject:typedOther.contactPhoneNumber]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.contactUUID equalToObject:typedOther.contactUUID]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.hasDismissedOffers != typedOther.hasDismissedOffers) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContactUUID:(nullable NSString *)contactUUID
|
||||
contactPhoneNumber:(nullable NSString *)contactPhoneNumber
|
||||
{
|
||||
NSString *uniqueId = [[self class] generateUniqueId];
|
||||
|
||||
if (self = [super initWithUniqueId:uniqueId]) {
|
||||
_contactUUID = [contactUUID copy];
|
||||
_contactPhoneNumber = [contactPhoneNumber copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (SignalServiceAddress *)contactAddress
|
||||
{
|
||||
return [[SignalServiceAddress alloc] initWithServiceIdString:self.contactUUID phoneNumber:self.contactPhoneNumber];
|
||||
}
|
||||
|
||||
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithTransaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
return @[ self.contactAddress ];
|
||||
}
|
||||
|
||||
- (BOOL)isNoteToSelf
|
||||
{
|
||||
return self.contactAddress.isLocalAddress;
|
||||
}
|
||||
|
||||
- (BOOL)hasSafetyNumbers
|
||||
{
|
||||
return [OWSIdentityManagerObjCBridge identityKeyForAddress:self.contactAddress] != nil;
|
||||
}
|
||||
|
||||
+ (nullable SignalServiceAddress *)contactAddressFromThreadId:(NSString *)threadId
|
||||
transaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
return [TSContactThread fetchContactThreadViaCacheWithUniqueId:threadId transaction:transaction].contactAddress;
|
||||
}
|
||||
|
||||
- (void)anyDidInsertWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidInsertWithTransaction:transaction];
|
||||
|
||||
OWSLogInfo(@"Inserted contact thread: %@", self.contactAddress);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -4,8 +4,177 @@
|
||||
//
|
||||
|
||||
import LibSignalClient
|
||||
public import GRDB
|
||||
|
||||
extension TSContactThread {
|
||||
open class TSContactThread: TSThread {
|
||||
override public class var recordType: SDSRecordType { .contactThread }
|
||||
|
||||
/// Represents the uppercase ServiceId string for this contact.
|
||||
/// - Note
|
||||
/// This property name includes `UUID` for compatibility with SDS (to match the
|
||||
/// SQLite column), but **may not contain a valid UUID string**.
|
||||
public internal(set) var contactUUID: String?
|
||||
public internal(set) var contactPhoneNumber: String?
|
||||
|
||||
public let hasDismissedOffers: Bool
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression {
|
||||
case contactPhoneNumber
|
||||
case contactUUID
|
||||
case hasDismissedOffers
|
||||
}
|
||||
|
||||
public required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.contactUUID = try container.decodeIfPresent(String.self, forKey: .contactUUID)
|
||||
self.contactPhoneNumber = try container.decodeIfPresent(String.self, forKey: .contactPhoneNumber)
|
||||
self.hasDismissedOffers = try container.decode(Bool.self, forKey: .hasDismissedOffers)
|
||||
try super.init(inheritableDecoder: decoder)
|
||||
}
|
||||
|
||||
override public func encode(to encoder: any Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.contactUUID, forKey: .contactUUID)
|
||||
try container.encode(self.contactPhoneNumber, forKey: .contactPhoneNumber)
|
||||
try container.encode(self.hasDismissedOffers, forKey: .hasDismissedOffers)
|
||||
}
|
||||
|
||||
override public var hash: Int {
|
||||
var hasher = Hasher()
|
||||
hasher.combine(super.hash)
|
||||
hasher.combine(self.contactPhoneNumber)
|
||||
hasher.combine(self.contactUUID)
|
||||
hasher.combine(self.hasDismissedOffers)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? Self else { return false }
|
||||
guard super.isEqual(object) else { return false }
|
||||
guard self.contactPhoneNumber == object.contactPhoneNumber else { return false }
|
||||
guard self.contactUUID == object.contactUUID else { return false }
|
||||
guard self.hasDismissedOffers == object.hasDismissedOffers else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
init(
|
||||
id: Int64?,
|
||||
uniqueId: String,
|
||||
conversationColorNameObsolete: String,
|
||||
creationDate: Date?,
|
||||
editTargetTimestamp: UInt64?,
|
||||
isArchivedObsolete: Bool,
|
||||
isMarkedUnreadObsolete: Bool,
|
||||
lastDraftInteractionRowId: UInt64,
|
||||
lastDraftUpdateTimestamp: UInt64,
|
||||
lastInteractionRowId: UInt64,
|
||||
lastSentStoryTimestamp: UInt64?,
|
||||
lastVisibleSortIdObsolete: UInt64,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: Double,
|
||||
mentionNotificationMode: TSThreadMentionNotificationMode,
|
||||
messageDraft: String?,
|
||||
messageDraftBodyRanges: MessageBodyRanges?,
|
||||
mutedUntilDateObsolete: Date?,
|
||||
mutedUntilTimestampObsolete: UInt64,
|
||||
shouldThreadBeVisible: Bool,
|
||||
storyViewMode: TSThreadStoryViewMode,
|
||||
contactUUID: String?,
|
||||
contactPhoneNumber: String?,
|
||||
hasDismissedOffers: Bool,
|
||||
) {
|
||||
self.contactUUID = contactUUID
|
||||
self.contactPhoneNumber = contactPhoneNumber
|
||||
self.hasDismissedOffers = hasDismissedOffers
|
||||
super.init(
|
||||
id: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
uniqueId: String = UUID().uuidString,
|
||||
contactUUID: String?,
|
||||
contactPhoneNumber: String?,
|
||||
) {
|
||||
self.contactUUID = contactUUID
|
||||
self.contactPhoneNumber = contactPhoneNumber
|
||||
self.hasDismissedOffers = false
|
||||
super.init(uniqueId: uniqueId)
|
||||
}
|
||||
|
||||
override func deepCopy() -> TSThread {
|
||||
return TSContactThread(
|
||||
id: self.id,
|
||||
uniqueId: self.uniqueId,
|
||||
conversationColorNameObsolete: self.conversationColorNameObsolete,
|
||||
creationDate: self.creationDate,
|
||||
editTargetTimestamp: self.editTargetTimestamp,
|
||||
isArchivedObsolete: self.isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: self.isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: self.lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: self.lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: self.lastInteractionRowId,
|
||||
lastSentStoryTimestamp: self.lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: self.lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: self.lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: self.mentionNotificationMode,
|
||||
messageDraft: self.messageDraft,
|
||||
messageDraftBodyRanges: self.messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: self.mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: self.mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: self.shouldThreadBeVisible,
|
||||
storyViewMode: self.storyViewMode,
|
||||
contactUUID: self.contactUUID,
|
||||
contactPhoneNumber: self.contactPhoneNumber,
|
||||
hasDismissedOffers: self.hasDismissedOffers,
|
||||
)
|
||||
}
|
||||
|
||||
class func fetchContactThreadViaCache(uniqueId: String, transaction: DBReadTransaction) -> TSContactThread? {
|
||||
return fetchViaCache(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
public var contactAddress: SignalServiceAddress {
|
||||
return SignalServiceAddress(serviceIdString: self.contactUUID, phoneNumber: self.contactPhoneNumber)
|
||||
}
|
||||
|
||||
override public func recipientAddresses(with tx: DBReadTransaction) -> [SignalServiceAddress] {
|
||||
return [self.contactAddress]
|
||||
}
|
||||
|
||||
override public var isNoteToSelf: Bool { self.contactAddress.isLocalAddress }
|
||||
|
||||
override public func hasSafetyNumbers() -> Bool {
|
||||
return OWSIdentityManagerObjCBridge.identityKey(forAddress: self.contactAddress) != nil
|
||||
}
|
||||
|
||||
static func contactAddress(fromThreadId threadUniqueId: String, transaction tx: DBReadTransaction) -> SignalServiceAddress? {
|
||||
return (TSThread.fetchViaCache(uniqueId: threadUniqueId, transaction: tx) as? TSContactThread)?.contactAddress
|
||||
}
|
||||
|
||||
override public func anyDidInsert(transaction: DBWriteTransaction) {
|
||||
super.anyDidInsert(transaction: transaction)
|
||||
Logger.info("Inserted contact thread: \(self.contactAddress)")
|
||||
}
|
||||
|
||||
@objc
|
||||
public convenience init(contactAddress: SignalServiceAddress) {
|
||||
@ -88,3 +257,15 @@ extension TSContactThread {
|
||||
return ContactThreadFinder().contactThread(for: contactAddress, tx: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StringInterpolation
|
||||
|
||||
public extension String.StringInterpolation {
|
||||
mutating func appendInterpolation(contactThreadColumn column: TSContactThread.CodingKeys) {
|
||||
appendLiteral(column.rawValue)
|
||||
}
|
||||
|
||||
mutating func appendInterpolation(contactThreadColumnFullyQualified column: TSContactThread.CodingKeys) {
|
||||
appendLiteral("\(TSThread.databaseTableName).\(column.rawValue)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
|
||||
// Do not manually edit it, instead run `sds_codegen.sh`.
|
||||
|
||||
// MARK: - Typed Convenience Methods
|
||||
|
||||
@objc
|
||||
public extension TSGroupThread {
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
class func fetchGroupThreadViaCache(
|
||||
uniqueId: String,
|
||||
transaction: DBReadTransaction
|
||||
) -> TSGroupThread? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
guard let object = fetchViaCache(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return nil
|
||||
}
|
||||
guard let instance = object as? TSGroupThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return nil
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
func anyUpdateGroupThread(transaction: DBWriteTransaction, block: (TSGroupThread) -> Void) {
|
||||
anyUpdate(transaction: transaction) { (object) in
|
||||
guard let instance = object as? TSGroupThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return
|
||||
}
|
||||
block(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSSerializer
|
||||
|
||||
// The SDSSerializer protocol specifies how to insert and update the
|
||||
// row that corresponds to this model.
|
||||
class TSGroupThreadSerializer: SDSSerializer {
|
||||
|
||||
private let model: TSGroupThread
|
||||
public init(model: TSGroupThread) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
func asRecord() -> SDSRecord {
|
||||
let id: Int64? = model.grdbId?.int64Value
|
||||
|
||||
let recordType: SDSRecordType = .groupThread
|
||||
let uniqueId: String = model.uniqueId
|
||||
|
||||
// Properties
|
||||
let conversationColorName: String = model.conversationColorNameObsolete
|
||||
let creationDate: Double? = archiveOptionalDate(model.creationDate)
|
||||
let isArchived: Bool = model.isArchivedObsolete
|
||||
let lastInteractionRowId: UInt64 = model.lastInteractionRowId
|
||||
let messageDraft: String? = model.messageDraft
|
||||
let mutedUntilDate: Double? = archiveOptionalDate(model.mutedUntilDateObsolete)
|
||||
let shouldThreadBeVisible: Bool = model.shouldThreadBeVisible
|
||||
let contactPhoneNumber: String? = nil
|
||||
let contactUUID: String? = nil
|
||||
let groupModel: Data? = optionalArchive(model.groupModel)
|
||||
let hasDismissedOffers: Bool? = nil
|
||||
let isMarkedUnread: Bool = model.isMarkedUnreadObsolete
|
||||
let lastVisibleSortIdOnScreenPercentage: Double = model.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let lastVisibleSortId: UInt64 = model.lastVisibleSortIdObsolete
|
||||
let messageDraftBodyRanges: Data? = optionalArchive(model.messageDraftBodyRanges)
|
||||
let mentionNotificationMode: UInt = model.mentionNotificationMode.rawValue
|
||||
let mutedUntilTimestamp: UInt64 = model.mutedUntilTimestampObsolete
|
||||
let allowsReplies: Bool? = nil
|
||||
let lastSentStoryTimestamp: UInt64? = archiveOptionalNSNumber(model.lastSentStoryTimestamp, conversion: { $0.uint64Value })
|
||||
let name: String? = nil
|
||||
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, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/TSGroupModel.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class DBReadTransaction;
|
||||
@class DBWriteTransaction;
|
||||
@class MessageBodyRanges;
|
||||
@class TSGroupModelV2;
|
||||
|
||||
extern NSString *const TSGroupThreadAvatarChangedNotification;
|
||||
extern NSString *const TSGroupThread_NotificationKey_UniqueId;
|
||||
|
||||
@interface TSGroupThread : TSThread
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithGroupModel:(TSGroupModelV2 *)groupModel NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode NS_UNAVAILABLE;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
groupModel:(TSGroupModel *)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
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
@property (nonatomic) TSGroupModel *groupModel;
|
||||
@property (nonatomic, readonly) NSString *groupNameOrDefault;
|
||||
@property (nonatomic, readonly, class) NSString *defaultGroupName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,168 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "TSGroupThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const TSGroupThreadAvatarChangedNotification = @"TSGroupThreadAvatarChangedNotification";
|
||||
NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_NotificationKey_UniqueId";
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation TSGroupThread
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
groupModel:(TSGroupModel *)groupModel
|
||||
{
|
||||
self = [super initWithGrdbId:grdbId
|
||||
uniqueId:uniqueId
|
||||
conversationColorNameObsolete:conversationColorNameObsolete
|
||||
creationDate:creationDate
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:mentionNotificationMode
|
||||
messageDraft:messageDraft
|
||||
messageDraftBodyRanges:messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:shouldThreadBeVisible
|
||||
storyViewMode:storyViewMode];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_groupModel = groupModel;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = [super hash];
|
||||
result ^= self.groupModel.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![super isEqual:other]) {
|
||||
return NO;
|
||||
}
|
||||
TSGroupThread *typedOther = (TSGroupThread *)other;
|
||||
if (![NSObject isObject:self.groupModel equalToObject:typedOther.groupModel]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithGroupModel:(TSGroupModelV2 *)groupModel
|
||||
{
|
||||
OWSAssertDebug(groupModel);
|
||||
OWSAssertDebug(groupModel.groupId.length > 0);
|
||||
#ifdef DEBUG
|
||||
for (SignalServiceAddress *address in groupModel.groupMembers) {
|
||||
OWSAssertDebug(address.isValid);
|
||||
}
|
||||
#endif
|
||||
|
||||
NSString *uniqueIdentifier = [[self class] defaultThreadIdForGroupId:groupModel.groupId];
|
||||
self = [super initWithUniqueId:uniqueIdentifier];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_groupModel = groupModel;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithTransaction:(DBReadTransaction *)transaction
|
||||
{
|
||||
NSMutableArray<SignalServiceAddress *> *groupMembers = [self.groupModel.groupMembers mutableCopy];
|
||||
if (groupMembers == nil) {
|
||||
return @[];
|
||||
}
|
||||
|
||||
[groupMembers removeObject:[TSAccountManagerObjcBridge localAciAddressWith:transaction]];
|
||||
|
||||
return [groupMembers copy];
|
||||
}
|
||||
|
||||
- (NSString *)groupNameOrDefault
|
||||
{
|
||||
return self.groupModel.groupNameOrDefault;
|
||||
}
|
||||
|
||||
+ (NSString *)defaultGroupName
|
||||
{
|
||||
return OWSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)anyWillInsertWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyWillInsertWithTransaction:transaction];
|
||||
[self updateGroupMemberRecordsWithTransaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyWillUpdateWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyWillUpdateWithTransaction:transaction];
|
||||
|
||||
// We used to update the group member records here, but there are many updates that don't touch membership.
|
||||
// Now it's done explicitly where we update the group model, and not for other updates.
|
||||
}
|
||||
|
||||
- (void)anyDidInsertWithTransaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidInsertWithTransaction:transaction];
|
||||
|
||||
OWSLogInfo(@"Inserted group thread: %@", self.groupId.hexadecimalString);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -4,8 +4,181 @@
|
||||
//
|
||||
|
||||
public import LibSignalClient
|
||||
public import GRDB
|
||||
|
||||
extension Notification.Name {
|
||||
public static let TSGroupThreadAvatarChanged = Notification.Name("TSGroupThreadAvatarChangedNotification")
|
||||
}
|
||||
|
||||
public let TSGroupThread_NotificationKey_UniqueId = "TSGroupThread_NotificationKey_UniqueId"
|
||||
|
||||
open class TSGroupThread: TSThread {
|
||||
override public class var recordType: SDSRecordType { .groupThread }
|
||||
|
||||
public private(set) var groupModel: TSGroupModel
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression {
|
||||
case groupModel
|
||||
}
|
||||
|
||||
public required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let groupModelData = try container.decode(Data.self, forKey: .groupModel)
|
||||
self.groupModel = try LegacySDSSerializer().deserializeLegacySDSData(groupModelData, ofClass: TSGroupModel.self)
|
||||
try super.init(inheritableDecoder: decoder)
|
||||
}
|
||||
|
||||
override public func encode(to encoder: any Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.groupModel), forKey: .groupModel)
|
||||
}
|
||||
|
||||
override public var hash: Int {
|
||||
var hasher = Hasher()
|
||||
hasher.combine(super.hash)
|
||||
hasher.combine(self.groupModel)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? Self else { return false }
|
||||
guard super.isEqual(object) else { return false }
|
||||
guard self.groupModel == object.groupModel else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
init(
|
||||
id: Int64?,
|
||||
uniqueId: String,
|
||||
conversationColorNameObsolete: String,
|
||||
creationDate: Date?,
|
||||
editTargetTimestamp: UInt64?,
|
||||
isArchivedObsolete: Bool,
|
||||
isMarkedUnreadObsolete: Bool,
|
||||
lastDraftInteractionRowId: UInt64,
|
||||
lastDraftUpdateTimestamp: UInt64,
|
||||
lastInteractionRowId: UInt64,
|
||||
lastSentStoryTimestamp: UInt64?,
|
||||
lastVisibleSortIdObsolete: UInt64,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: Double,
|
||||
mentionNotificationMode: TSThreadMentionNotificationMode,
|
||||
messageDraft: String?,
|
||||
messageDraftBodyRanges: MessageBodyRanges?,
|
||||
mutedUntilDateObsolete: Date?,
|
||||
mutedUntilTimestampObsolete: UInt64,
|
||||
shouldThreadBeVisible: Bool,
|
||||
storyViewMode: TSThreadStoryViewMode,
|
||||
groupModel: TSGroupModel,
|
||||
) {
|
||||
self.groupModel = groupModel
|
||||
super.init(
|
||||
id: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
)
|
||||
}
|
||||
|
||||
public init(
|
||||
uniqueId: String,
|
||||
groupModel: TSGroupModelV2,
|
||||
) {
|
||||
owsAssertDebug(!groupModel.groupId.isEmpty)
|
||||
#if DEBUG
|
||||
groupModel.groupMembers.forEach({ owsAssertDebug($0.isValid) })
|
||||
#endif
|
||||
|
||||
self.groupModel = groupModel
|
||||
super.init(uniqueId: uniqueId)
|
||||
}
|
||||
|
||||
public convenience init(groupModel: TSGroupModelV2) {
|
||||
self.init(
|
||||
uniqueId: Self.defaultThreadId(forGroupId: groupModel.groupId),
|
||||
groupModel: groupModel,
|
||||
)
|
||||
}
|
||||
|
||||
override func deepCopy() -> TSThread {
|
||||
return TSGroupThread(
|
||||
id: self.id,
|
||||
uniqueId: self.uniqueId,
|
||||
conversationColorNameObsolete: self.conversationColorNameObsolete,
|
||||
creationDate: self.creationDate,
|
||||
editTargetTimestamp: self.editTargetTimestamp,
|
||||
isArchivedObsolete: self.isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: self.isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: self.lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: self.lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: self.lastInteractionRowId,
|
||||
lastSentStoryTimestamp: self.lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: self.lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: self.lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: self.mentionNotificationMode,
|
||||
messageDraft: self.messageDraft,
|
||||
messageDraftBodyRanges: self.messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: self.mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: self.mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: self.shouldThreadBeVisible,
|
||||
storyViewMode: self.storyViewMode,
|
||||
groupModel: self.groupModel,
|
||||
)
|
||||
}
|
||||
|
||||
public class func fetchGroupThreadViaCache(uniqueId: String, transaction: DBReadTransaction) -> TSGroupThread? {
|
||||
return fetchViaCache(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
override public func recipientAddresses(with tx: DBReadTransaction) -> [SignalServiceAddress] {
|
||||
var groupMembers = self.groupModel.groupMembers
|
||||
groupMembers.removeAll(where: { $0.isLocalAddress })
|
||||
return groupMembers
|
||||
}
|
||||
|
||||
public var groupNameOrDefault: String {
|
||||
return self.groupModel.groupNameOrDefault
|
||||
}
|
||||
|
||||
@objc
|
||||
public class var defaultGroupName: String {
|
||||
return OWSLocalizedString("NEW_GROUP_DEFAULT_TITLE", comment: "")
|
||||
}
|
||||
|
||||
override open func anyWillInsert(transaction: DBWriteTransaction) {
|
||||
super.anyWillInsert(transaction: transaction)
|
||||
updateGroupMemberRecords(transaction: transaction)
|
||||
}
|
||||
|
||||
override public func anyWillUpdate(transaction: DBWriteTransaction) {
|
||||
super.anyWillUpdate(transaction: transaction)
|
||||
|
||||
// We used to update the group member records here, but there are many
|
||||
// updates that don't touch membership. Now it's done explicitly where we
|
||||
// update the group model, and not for other updates.
|
||||
}
|
||||
|
||||
override public func anyDidInsert(transaction: DBWriteTransaction) {
|
||||
super.anyDidInsert(transaction: transaction)
|
||||
Logger.info("Inserted group thread: \(self.groupId.hexadecimalString)")
|
||||
}
|
||||
|
||||
extension TSGroupThread {
|
||||
func update(
|
||||
with newGroupModel: TSGroupModel,
|
||||
transaction tx: DBWriteTransaction,
|
||||
@ -15,7 +188,7 @@ extension TSGroupThread {
|
||||
|
||||
let oldGroupMembers = groupModel.groupMembers
|
||||
|
||||
anyUpdateGroupThread(transaction: tx) { groupThread in
|
||||
anyUpdate(transaction: tx) { groupThread in
|
||||
if let oldGroupModelV2 = groupThread.groupModel as? TSGroupModelV2 {
|
||||
if let newGroupModelV2 = newGroupModel as? TSGroupModelV2 {
|
||||
owsPrecondition(oldGroupModelV2.revision <= newGroupModelV2.revision)
|
||||
@ -135,7 +308,7 @@ extension TSGroupThread {
|
||||
|
||||
// MARK: -
|
||||
|
||||
override open func updateWithInsertedInteraction(
|
||||
override public func updateWithInsertedInteraction(
|
||||
_ interaction: TSInteraction,
|
||||
tx: DBWriteTransaction,
|
||||
) {
|
||||
@ -203,7 +376,7 @@ extension TSGroupThread {
|
||||
) -> TSGroupThread {
|
||||
let groupThreadId = TSGroupThread.defaultThreadId(forGroupId: groupId)
|
||||
let groupThread = TSGroupThread(
|
||||
grdbId: 1,
|
||||
id: 1,
|
||||
uniqueId: groupThreadId,
|
||||
conversationColorNameObsolete: "",
|
||||
creationDate: nil,
|
||||
@ -241,7 +414,7 @@ extension TSGroupThread {
|
||||
addedByAddress: nil,
|
||||
),
|
||||
)
|
||||
groupThread.clearRowId()
|
||||
groupThread.id = nil
|
||||
return groupThread
|
||||
}
|
||||
#endif
|
||||
@ -278,3 +451,15 @@ public extension TSThreadStoryViewMode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StringInterpolation
|
||||
|
||||
public extension String.StringInterpolation {
|
||||
mutating func appendInterpolation(groupThreadColumn column: TSGroupThread.CodingKeys) {
|
||||
appendLiteral(column.rawValue)
|
||||
}
|
||||
|
||||
mutating func appendInterpolation(groupThreadColumnFullyQualified column: TSGroupThread.CodingKeys) {
|
||||
appendLiteral("\(TSThread.databaseTableName).\(column.rawValue)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
|
||||
// Do not manually edit it, instead run `sds_codegen.sh`.
|
||||
|
||||
// MARK: - Typed Convenience Methods
|
||||
|
||||
@objc
|
||||
public extension TSPrivateStoryThread {
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
class func fetchPrivateStoryThreadViaCache(
|
||||
uniqueId: String,
|
||||
transaction: DBReadTransaction
|
||||
) -> TSPrivateStoryThread? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
guard let object = fetchViaCache(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return nil
|
||||
}
|
||||
guard let instance = object as? TSPrivateStoryThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return nil
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
// NOTE: This method will fail if the object has unexpected type.
|
||||
func anyUpdatePrivateStoryThread(transaction: DBWriteTransaction, block: (TSPrivateStoryThread) -> Void) {
|
||||
anyUpdate(transaction: transaction) { (object) in
|
||||
guard let instance = object as? TSPrivateStoryThread else {
|
||||
owsFailDebug("Object has unexpected type: \(type(of: object))")
|
||||
return
|
||||
}
|
||||
block(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSSerializer
|
||||
|
||||
// The SDSSerializer protocol specifies how to insert and update the
|
||||
// row that corresponds to this model.
|
||||
class TSPrivateStoryThreadSerializer: SDSSerializer {
|
||||
|
||||
private let model: TSPrivateStoryThread
|
||||
public init(model: TSPrivateStoryThread) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
func asRecord() -> SDSRecord {
|
||||
let id: Int64? = model.grdbId?.int64Value
|
||||
|
||||
let recordType: SDSRecordType = .privateStoryThread
|
||||
let uniqueId: String = model.uniqueId
|
||||
|
||||
// Properties
|
||||
let conversationColorName: String = model.conversationColorNameObsolete
|
||||
let creationDate: Double? = archiveOptionalDate(model.creationDate)
|
||||
let isArchived: Bool = model.isArchivedObsolete
|
||||
let lastInteractionRowId: UInt64 = model.lastInteractionRowId
|
||||
let messageDraft: String? = model.messageDraft
|
||||
let mutedUntilDate: Double? = archiveOptionalDate(model.mutedUntilDateObsolete)
|
||||
let shouldThreadBeVisible: Bool = model.shouldThreadBeVisible
|
||||
let contactPhoneNumber: String? = nil
|
||||
let contactUUID: String? = nil
|
||||
let groupModel: Data? = nil
|
||||
let hasDismissedOffers: Bool? = nil
|
||||
let isMarkedUnread: Bool = model.isMarkedUnreadObsolete
|
||||
let lastVisibleSortIdOnScreenPercentage: Double = model.lastVisibleSortIdOnScreenPercentageObsolete
|
||||
let lastVisibleSortId: UInt64 = model.lastVisibleSortIdObsolete
|
||||
let messageDraftBodyRanges: Data? = optionalArchive(model.messageDraftBodyRanges)
|
||||
let mentionNotificationMode: UInt = model.mentionNotificationMode.rawValue
|
||||
let mutedUntilTimestamp: UInt64 = model.mutedUntilTimestampObsolete
|
||||
let allowsReplies: Bool? = model.allowsReplies
|
||||
let lastSentStoryTimestamp: UInt64? = archiveOptionalNSNumber(model.lastSentStoryTimestamp, conversion: { $0.uint64Value })
|
||||
let name: String? = model.name
|
||||
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, lastDraftInteractionRowId: lastDraftInteractionRowId, lastDraftUpdateTimestamp: lastDraftUpdateTimestamp)
|
||||
}
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/SignalServiceKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class SignalServiceAddress;
|
||||
|
||||
/// Represents a story distribution list.
|
||||
@interface TSPrivateStoryThread : TSThread
|
||||
|
||||
@property (nonatomic) BOOL allowsReplies;
|
||||
@property (nonatomic) NSString *name;
|
||||
|
||||
/// deprecated
|
||||
@property (nonatomic, nullable) NSData *addresses;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isMyStory;
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId
|
||||
name:(NSString *)name
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
viewMode:(TSThreadStoryViewMode)viewMode NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
viewMode:(TSThreadStoryViewMode)viewMode NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode NS_UNAVAILABLE;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
addresses:(nullable NSData *)addresses
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
name:(NSString *)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
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,146 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "TSPrivateStoryThread.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
@implementation TSPrivateStoryThread
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId
|
||||
name:(NSString *)name
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
viewMode:(TSThreadStoryViewMode)viewMode
|
||||
{
|
||||
self = [super initWithUniqueId:uniqueId];
|
||||
if (self) {
|
||||
self.name = name;
|
||||
self.allowsReplies = allowsReplies;
|
||||
self.storyViewMode = viewMode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = [super hash];
|
||||
result ^= self.addresses.hash;
|
||||
result ^= self.allowsReplies;
|
||||
result ^= self.name.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![super isEqual:other]) {
|
||||
return NO;
|
||||
}
|
||||
TSPrivateStoryThread *typedOther = (TSPrivateStoryThread *)other;
|
||||
if (![NSObject isObject:self.addresses equalToObject:typedOther.addresses]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.allowsReplies != typedOther.allowsReplies) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.name equalToObject:typedOther.name]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name allowsReplies:(BOOL)allowsReplies viewMode:(TSThreadStoryViewMode)viewMode
|
||||
{
|
||||
NSString *uniqueId = [[self class] generateUniqueId];
|
||||
self = [super initWithUniqueId:uniqueId];
|
||||
if (self) {
|
||||
self.name = name;
|
||||
self.allowsReplies = allowsReplies;
|
||||
self.storyViewMode = viewMode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
|
||||
creationDate:(nullable NSDate *)creationDate
|
||||
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
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
|
||||
messageDraft:(nullable NSString *)messageDraft
|
||||
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
|
||||
storyViewMode:(TSThreadStoryViewMode)storyViewMode
|
||||
addresses:(nullable NSData *)addresses
|
||||
allowsReplies:(BOOL)allowsReplies
|
||||
name:(NSString *)name
|
||||
{
|
||||
self = [super initWithGrdbId:grdbId
|
||||
uniqueId:uniqueId
|
||||
conversationColorNameObsolete:conversationColorNameObsolete
|
||||
creationDate:creationDate
|
||||
editTargetTimestamp:editTargetTimestamp
|
||||
isArchivedObsolete:isArchivedObsolete
|
||||
isMarkedUnreadObsolete:isMarkedUnreadObsolete
|
||||
lastDraftInteractionRowId:lastDraftInteractionRowId
|
||||
lastDraftUpdateTimestamp:lastDraftUpdateTimestamp
|
||||
lastInteractionRowId:lastInteractionRowId
|
||||
lastSentStoryTimestamp:lastSentStoryTimestamp
|
||||
lastVisibleSortIdObsolete:lastVisibleSortIdObsolete
|
||||
lastVisibleSortIdOnScreenPercentageObsolete:lastVisibleSortIdOnScreenPercentageObsolete
|
||||
mentionNotificationMode:mentionNotificationMode
|
||||
messageDraft:messageDraft
|
||||
messageDraftBodyRanges:messageDraftBodyRanges
|
||||
mutedUntilDateObsolete:mutedUntilDateObsolete
|
||||
mutedUntilTimestampObsolete:mutedUntilTimestampObsolete
|
||||
shouldThreadBeVisible:shouldThreadBeVisible
|
||||
storyViewMode:storyViewMode];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_addresses = addresses;
|
||||
_allowsReplies = allowsReplies;
|
||||
_name = name;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
- (BOOL)isMyStory
|
||||
{
|
||||
return [self.uniqueId isEqualToString:[self class].myStoryUniqueId];
|
||||
}
|
||||
|
||||
- (NSString *)name
|
||||
{
|
||||
if (self.isMyStory) {
|
||||
return OWSLocalizedString(
|
||||
@"MY_STORY_NAME", @"Name for the 'My Story' default story that sends to all the user's contacts.");
|
||||
}
|
||||
|
||||
return _name;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -4,8 +4,160 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public import GRDB
|
||||
|
||||
/// Represents a story distribution list.
|
||||
public final class TSPrivateStoryThread: TSThread {
|
||||
override public class var recordType: SDSRecordType { .privateStoryThread }
|
||||
|
||||
public private(set) var allowsReplies: Bool
|
||||
public private(set) var _name: String
|
||||
/// deprecated
|
||||
public let addresses: Data?
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression {
|
||||
case allowsReplies
|
||||
case name
|
||||
case addresses
|
||||
}
|
||||
|
||||
required init(inheritableDecoder decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.allowsReplies = try container.decode(Bool.self, forKey: .allowsReplies)
|
||||
self._name = try container.decode(String.self, forKey: .name)
|
||||
self.addresses = try container.decodeIfPresent(Data.self, forKey: .addresses)
|
||||
try super.init(inheritableDecoder: decoder)
|
||||
}
|
||||
|
||||
override public func encode(to encoder: any Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.allowsReplies, forKey: .allowsReplies)
|
||||
try container.encode(self._name, forKey: .name)
|
||||
try container.encode(self.addresses, forKey: .addresses)
|
||||
}
|
||||
|
||||
init(
|
||||
id: Int64?,
|
||||
uniqueId: String,
|
||||
conversationColorNameObsolete: String,
|
||||
creationDate: Date?,
|
||||
editTargetTimestamp: UInt64?,
|
||||
isArchivedObsolete: Bool,
|
||||
isMarkedUnreadObsolete: Bool,
|
||||
lastDraftInteractionRowId: UInt64,
|
||||
lastDraftUpdateTimestamp: UInt64,
|
||||
lastInteractionRowId: UInt64,
|
||||
lastSentStoryTimestamp: UInt64?,
|
||||
lastVisibleSortIdObsolete: UInt64,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: Double,
|
||||
mentionNotificationMode: TSThreadMentionNotificationMode,
|
||||
messageDraft: String?,
|
||||
messageDraftBodyRanges: MessageBodyRanges?,
|
||||
mutedUntilDateObsolete: Date?,
|
||||
mutedUntilTimestampObsolete: UInt64,
|
||||
shouldThreadBeVisible: Bool,
|
||||
storyViewMode: TSThreadStoryViewMode,
|
||||
allowsReplies: Bool,
|
||||
name: String,
|
||||
addresses: Data?,
|
||||
) {
|
||||
self.allowsReplies = allowsReplies
|
||||
self._name = name
|
||||
self.addresses = addresses
|
||||
super.init(
|
||||
id: id,
|
||||
uniqueId: uniqueId,
|
||||
conversationColorNameObsolete: conversationColorNameObsolete,
|
||||
creationDate: creationDate,
|
||||
editTargetTimestamp: editTargetTimestamp,
|
||||
isArchivedObsolete: isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowId,
|
||||
lastSentStoryTimestamp: lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: mentionNotificationMode,
|
||||
messageDraft: messageDraft,
|
||||
messageDraftBodyRanges: messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: shouldThreadBeVisible,
|
||||
storyViewMode: storyViewMode,
|
||||
)
|
||||
}
|
||||
|
||||
public init(uniqueId: String = UUID().uuidString, name: String, allowsReplies: Bool, viewMode: TSThreadStoryViewMode) {
|
||||
self._name = name
|
||||
self.allowsReplies = allowsReplies
|
||||
self.addresses = nil
|
||||
super.init(uniqueId: uniqueId)
|
||||
self.storyViewMode = viewMode
|
||||
}
|
||||
|
||||
override func deepCopy() -> TSThread {
|
||||
return TSPrivateStoryThread(
|
||||
id: self.id,
|
||||
uniqueId: self.uniqueId,
|
||||
conversationColorNameObsolete: self.conversationColorNameObsolete,
|
||||
creationDate: self.creationDate,
|
||||
editTargetTimestamp: self.editTargetTimestamp,
|
||||
isArchivedObsolete: self.isArchivedObsolete,
|
||||
isMarkedUnreadObsolete: self.isMarkedUnreadObsolete,
|
||||
lastDraftInteractionRowId: self.lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: self.lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: self.lastInteractionRowId,
|
||||
lastSentStoryTimestamp: self.lastSentStoryTimestamp,
|
||||
lastVisibleSortIdObsolete: self.lastVisibleSortIdObsolete,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: self.lastVisibleSortIdOnScreenPercentageObsolete,
|
||||
mentionNotificationMode: self.mentionNotificationMode,
|
||||
messageDraft: self.messageDraft,
|
||||
messageDraftBodyRanges: self.messageDraftBodyRanges,
|
||||
mutedUntilDateObsolete: self.mutedUntilDateObsolete,
|
||||
mutedUntilTimestampObsolete: self.mutedUntilTimestampObsolete,
|
||||
shouldThreadBeVisible: self.shouldThreadBeVisible,
|
||||
storyViewMode: self.storyViewMode,
|
||||
allowsReplies: self.allowsReplies,
|
||||
name: self.name,
|
||||
addresses: self.addresses,
|
||||
)
|
||||
}
|
||||
|
||||
override public var hash: Int {
|
||||
var hasher = Hasher()
|
||||
hasher.combine(super.hash)
|
||||
hasher.combine(self.addresses)
|
||||
hasher.combine(self.allowsReplies)
|
||||
hasher.combine(self.name)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? Self else { return false }
|
||||
guard super.isEqual(object) else { return false }
|
||||
guard self.addresses == object.addresses else { return false }
|
||||
guard self.allowsReplies == object.allowsReplies else { return false }
|
||||
guard self.name == object.name else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public class func fetchPrivateStoryThreadViaCache(uniqueId: String, transaction: DBReadTransaction) -> TSPrivateStoryThread? {
|
||||
return fetchViaCache(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
public var isMyStory: Bool {
|
||||
return self.uniqueId == Self.myStoryUniqueId
|
||||
}
|
||||
|
||||
public var name: String {
|
||||
if self.isMyStory {
|
||||
return OWSLocalizedString("MY_STORY_NAME", comment: "Name for the 'My Story' default story that sends to all the user's contacts.")
|
||||
}
|
||||
return _name
|
||||
}
|
||||
|
||||
extension TSPrivateStoryThread {
|
||||
public typealias RowId = Int64
|
||||
|
||||
@objc
|
||||
@ -60,7 +212,7 @@ extension TSPrivateStoryThread {
|
||||
updateStorageService: Bool,
|
||||
transaction tx: DBWriteTransaction,
|
||||
) {
|
||||
anyUpdatePrivateStoryThread(transaction: tx) { privateStoryThread in
|
||||
anyUpdate(transaction: tx) { privateStoryThread in
|
||||
privateStoryThread.allowsReplies = allowsReplies
|
||||
}
|
||||
|
||||
@ -76,8 +228,8 @@ extension TSPrivateStoryThread {
|
||||
updateStorageService: Bool,
|
||||
transaction tx: DBWriteTransaction,
|
||||
) {
|
||||
anyUpdatePrivateStoryThread(transaction: tx) { privateStoryThread in
|
||||
privateStoryThread.name = name
|
||||
anyUpdate(transaction: tx) { privateStoryThread in
|
||||
privateStoryThread._name = name
|
||||
}
|
||||
|
||||
if updateStorageService, let distributionListIdentifier {
|
||||
@ -113,7 +265,7 @@ extension TSPrivateStoryThread {
|
||||
)
|
||||
}
|
||||
|
||||
anyUpdatePrivateStoryThread(transaction: tx) { privateStoryThread in
|
||||
anyUpdate(transaction: tx) { privateStoryThread in
|
||||
privateStoryThread.storyViewMode = storyViewMode
|
||||
}
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ extension TSThread {
|
||||
@objc
|
||||
public func editTarget(transaction: DBReadTransaction) -> TSOutgoingMessage? {
|
||||
guard
|
||||
let editTargetTimestamp = editTargetTimestamp?.uint64Value,
|
||||
let editTargetTimestamp,
|
||||
let localAddress = DependenciesBridge.shared.tsAccountManager.localIdentifiers(tx: transaction)?.aciAddress
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -327,13 +327,13 @@ public class MockThreadStore: ThreadStore {
|
||||
}
|
||||
|
||||
public func insertThread(_ thread: TSThread) {
|
||||
thread.updateRowId(nextRowId)
|
||||
thread.id = nextRowId
|
||||
threads.append(thread)
|
||||
nextRowId += 1
|
||||
}
|
||||
|
||||
public func fetchThread(rowId threadRowId: Int64, tx: DBReadTransaction) -> TSThread? {
|
||||
threads.first(where: { $0.sqliteRowId == threadRowId })
|
||||
threads.first(where: { $0.id == threadRowId })
|
||||
}
|
||||
|
||||
public func fetchThread(uniqueId: String, tx: DBReadTransaction) -> TSThread? {
|
||||
|
||||
@ -356,7 +356,7 @@ public class ThreadReadCache: NSObject {
|
||||
}
|
||||
|
||||
override func copy(value: TSThread) throws -> TSThread {
|
||||
return try DeepCopies.deepCopy(value)
|
||||
return value.deepCopy()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ final class CallRecordQuerierTest: XCTestCase {
|
||||
|
||||
private func insertThread(db: Database) -> (thread: TSThread, threadRowId: Int64) {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
try! thread.asRecord().insert(db)
|
||||
try! thread.insert(db)
|
||||
return (thread, thread.sqliteRowId!)
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ final class CallRecordQuerierTest: XCTestCase {
|
||||
let (thread, threadRowId): (TSThread, Int64) = {
|
||||
if let knownThreadRowId {
|
||||
return (
|
||||
try! TSThread.fromRecord(ThreadRecord.fetchOne(db, key: knownThreadRowId)!),
|
||||
try! TSThread.fetchOne(db, key: knownThreadRowId)!,
|
||||
knownThreadRowId,
|
||||
)
|
||||
} else {
|
||||
|
||||
@ -45,7 +45,7 @@ final class CallRecordStoreTest: XCTestCase {
|
||||
let interaction = TSInteraction(timestamp: 0, receivedAtTimestamp: 0, thread: thread)
|
||||
|
||||
inMemoryDB.write { tx in
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
try! interaction.asRecord().insert(tx.database)
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ final class GroupCallRecordManagerTest: XCTestCase {
|
||||
|
||||
private func createInteraction() -> (TSGroupThread, OWSGroupCallMessage) {
|
||||
let thread = TSGroupThread.randomForTesting()
|
||||
thread.updateRowId(.maxRandom)
|
||||
thread.id = .maxRandom
|
||||
|
||||
let interaction = OWSGroupCallMessage(
|
||||
joinedMemberAcis: [],
|
||||
|
||||
@ -36,7 +36,7 @@ final class IndividualCallRecordManagerTest: XCTestCase {
|
||||
callType: RPRecentCallType = .incomingIncomplete,
|
||||
) -> (TSContactThread, TSCall) {
|
||||
let thread = TSContactThread(contactAddress: .isolatedRandomForTesting())
|
||||
thread.updateRowId(.maxRandom)
|
||||
thread.id = .maxRandom
|
||||
|
||||
let interaction = TSCall(callType: callType, offerType: .audio, thread: thread, sentAtTimestamp: .maxRandom)
|
||||
interaction.updateRowId(.maxRandom)
|
||||
|
||||
@ -20,7 +20,7 @@ final class DeletedCallRecordStoreTest: XCTestCase {
|
||||
let thread = TSThread(uniqueId: UUID().uuidString)
|
||||
|
||||
inMemoryDB.write { tx in
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
try! thread.insert(tx.database)
|
||||
}
|
||||
|
||||
return thread.sqliteRowId!
|
||||
|
||||
@ -97,7 +97,7 @@ struct BackupEnablementReminderMegaphoneTests {
|
||||
|
||||
db.write { tx in
|
||||
let db = tx.database
|
||||
try! contactThread.asRecord().insert(db)
|
||||
try! contactThread.insert(db)
|
||||
}
|
||||
|
||||
for i in 0..<2000 {
|
||||
@ -123,7 +123,7 @@ struct BackupEnablementReminderMegaphoneTests {
|
||||
|
||||
db.write { tx in
|
||||
let db = tx.database
|
||||
try! contactThread.asRecord().insert(db)
|
||||
try! contactThread.insert(db)
|
||||
}
|
||||
|
||||
for i in 0..<2000 {
|
||||
|
||||
@ -17,28 +17,8 @@ class EditManagerTests: SSKBaseTest {
|
||||
super.setUp()
|
||||
db = InMemoryDB()
|
||||
authorAci = Aci.constantForTesting("00000000-0000-4000-8000-000000000000")
|
||||
thread = TSThread(
|
||||
grdbId: 1,
|
||||
uniqueId: "1",
|
||||
conversationColorNameObsolete: "Obsolete",
|
||||
creationDate: nil,
|
||||
editTargetTimestamp: nil,
|
||||
isArchivedObsolete: false,
|
||||
isMarkedUnreadObsolete: false,
|
||||
lastDraftInteractionRowId: 0,
|
||||
lastDraftUpdateTimestamp: 0,
|
||||
lastInteractionRowId: 0,
|
||||
lastSentStoryTimestamp: nil,
|
||||
lastVisibleSortIdObsolete: 0,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: 0,
|
||||
mentionNotificationMode: .always,
|
||||
messageDraft: nil,
|
||||
messageDraftBodyRanges: nil,
|
||||
mutedUntilDateObsolete: nil,
|
||||
mutedUntilTimestampObsolete: 0,
|
||||
shouldThreadBeVisible: true,
|
||||
storyViewMode: .default,
|
||||
)
|
||||
thread = TSThread(uniqueId: "1")
|
||||
thread.id = 1
|
||||
}
|
||||
|
||||
func testBasicValidation() throws {
|
||||
|
||||
@ -54,8 +54,8 @@ struct PinnedMessageManagerTest {
|
||||
@Test
|
||||
func testFetchPinnedMessagesForThread() throws {
|
||||
let thread = db.write { tx in
|
||||
let thread = TSThread()
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
let thread = TSThread(uniqueId: "")
|
||||
try! thread.insert(tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
@ -82,8 +82,8 @@ struct PinnedMessageManagerTest {
|
||||
@Test
|
||||
func testSortedPinnedMessages() throws {
|
||||
let thread = db.write { tx in
|
||||
let thread = TSThread()
|
||||
try! thread.asRecord().insert(tx.database)
|
||||
let thread = TSThread(uniqueId: "")
|
||||
try! thread.insert(tx.database)
|
||||
return thread
|
||||
}
|
||||
|
||||
|
||||
@ -11,8 +11,6 @@ import XCTest
|
||||
class ThreadFinderTests: XCTestCase {
|
||||
private var db = InMemoryDB()
|
||||
private let threadFinder = ThreadFinder()
|
||||
private var contactThread1: TSContactThread!
|
||||
private var contactThread2: TSContactThread!
|
||||
|
||||
enum ChatListType: CaseIterable {
|
||||
case inbox
|
||||
@ -20,62 +18,37 @@ class ThreadFinderTests: XCTestCase {
|
||||
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,
|
||||
) -> TSContactThread {
|
||||
return TSContactThread(
|
||||
id: nil,
|
||||
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,
|
||||
conversationColorNameObsolete: "Obsolete",
|
||||
creationDate: Date.now,
|
||||
editTargetTimestamp: nil,
|
||||
isArchivedObsolete: false,
|
||||
isMarkedUnreadObsolete: false,
|
||||
lastDraftInteractionRowId: lastDraftInteractionRowId,
|
||||
lastDraftUpdateTimestamp: lastDraftUpdateTimestamp,
|
||||
lastInteractionRowId: lastInteractionRowID,
|
||||
lastSentStoryTimestamp: nil,
|
||||
lastVisibleSortIdObsolete: 0,
|
||||
lastVisibleSortIdOnScreenPercentageObsolete: 0.0,
|
||||
mentionNotificationMode: .default,
|
||||
messageDraft: draft,
|
||||
messageDraftBodyRanges: nil,
|
||||
mutedUntilDateObsolete: nil,
|
||||
mutedUntilTimestampObsolete: 0,
|
||||
shouldThreadBeVisible: true,
|
||||
storyViewMode: .default,
|
||||
contactUUID: nil,
|
||||
contactPhoneNumber: nil,
|
||||
hasDismissedOffers: false,
|
||||
)
|
||||
}
|
||||
|
||||
@ -100,7 +73,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// New draft.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 0,
|
||||
lastDraftInteractionRowId: 1,
|
||||
@ -116,7 +88,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// Non-draft that has more recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 0,
|
||||
@ -165,7 +136,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// New draft that is not the latest activity on the thread.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 3,
|
||||
lastDraftInteractionRowId: 1,
|
||||
@ -181,7 +151,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// Non-draft that has less recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 2,
|
||||
lastDraftInteractionRowId: 0,
|
||||
@ -230,7 +199,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// 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,
|
||||
@ -246,7 +214,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// Thread 2, has a more recent draft based on timestamp.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 2,
|
||||
@ -295,7 +262,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// New draft that is not the latest activity on the thread.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID1",
|
||||
contactThread: contactThread1,
|
||||
draft: "test draft",
|
||||
lastInteractionRowID: 1,
|
||||
lastDraftInteractionRowId: 2,
|
||||
@ -311,7 +277,6 @@ class ThreadFinderTests: XCTestCase {
|
||||
// Non-draft that has less recent lastInteractionRowID.
|
||||
try buildThreadRecord(
|
||||
uniqueID: "UUID2",
|
||||
contactThread: contactThread2,
|
||||
draft: nil,
|
||||
lastInteractionRowID: 3,
|
||||
lastDraftInteractionRowId: 0,
|
||||
|
||||
@ -53,8 +53,8 @@ struct PollManagerTest {
|
||||
private func insertIncomingPollMessage(question: String, timestamp: UInt64? = nil) -> TSIncomingMessage {
|
||||
db.write { tx in
|
||||
let db = tx.database
|
||||
if try! groupThread.asRecord().exists(db) == false {
|
||||
try! groupThread!.asRecord().insert(db)
|
||||
if try! groupThread.exists(db) == false {
|
||||
try! groupThread!.insert(db)
|
||||
}
|
||||
|
||||
let incomingMessage = createIncomingMessage(with: groupThread) { builder in
|
||||
@ -73,8 +73,8 @@ struct PollManagerTest {
|
||||
private func insertOutgoingPollMessage(question: String) -> TSOutgoingMessage {
|
||||
db.write { tx in
|
||||
let db = tx.database
|
||||
if try! groupThread.asRecord().exists(db) == false {
|
||||
try! groupThread!.asRecord().insert(db)
|
||||
if try! groupThread.exists(db) == false {
|
||||
try! groupThread!.insert(db)
|
||||
}
|
||||
|
||||
let outgoingMessage = TSOutgoingMessage(in: groupThread, question: question)
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
//
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import SignalServiceKit
|
||||
import XCTest
|
||||
|
||||
final class TSThreadTests: XCTestCase {
|
||||
func testInitCallsDesignatedInit() throws {
|
||||
let thread = TSThread()
|
||||
XCTAssertEqual(thread.conversationColorNameObsolete, "Obsolete")
|
||||
XCTAssertNil(thread.messageDraft)
|
||||
let now = Date()
|
||||
let creationDate = try XCTUnwrap(thread.creationDate)
|
||||
XCTAssertEqual(creationDate.timeIntervalSinceReferenceDate, now.timeIntervalSinceReferenceDate, accuracy: 0.01 /* 10 ms */ )
|
||||
}
|
||||
}
|
||||
@ -297,12 +297,12 @@ public struct StoryConversationItem {
|
||||
) -> UInt64 {
|
||||
if
|
||||
let thread = thread as? TSGroupThread,
|
||||
associatedData?.lastReceivedTimestamp ?? 0 > thread.lastSentStoryTimestamp?.uint64Value ?? 0
|
||||
associatedData?.lastReceivedTimestamp ?? 0 > thread.lastSentStoryTimestamp ?? 0
|
||||
{
|
||||
return associatedData?.lastReceivedTimestamp ?? 0
|
||||
}
|
||||
|
||||
return thread.lastSentStoryTimestamp?.uint64Value ?? 0
|
||||
return thread.lastSentStoryTimestamp ?? 0
|
||||
}
|
||||
|
||||
let threads = ThreadFinder().storyThreads(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user