WIP
This commit is contained in:
parent
15c6784f0f
commit
aba4cda143
|
@ -3,25 +3,29 @@ import SessionUtilitiesKit
|
||||||
public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
||||||
public var delegate: JobDelegate?
|
public var delegate: JobDelegate?
|
||||||
private let data: Data
|
private let data: Data
|
||||||
|
private let messageServerID: UInt64?
|
||||||
public var failureCount: UInt = 0
|
public var failureCount: UInt = 0
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: Settings
|
||||||
public static let maxFailureCount: UInt = 10
|
public static let maxFailureCount: UInt = 10
|
||||||
|
|
||||||
// MARK: Initialization
|
// MARK: Initialization
|
||||||
public init(data: Data) {
|
public init(data: Data, messageServerID: UInt64? = nil) {
|
||||||
self.data = data
|
self.data = data
|
||||||
|
self.messageServerID = messageServerID
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Coding
|
// MARK: Coding
|
||||||
public init?(coder: NSCoder) {
|
public init?(coder: NSCoder) {
|
||||||
guard let data = coder.decodeObject(forKey: "data") as! Data? else { return nil }
|
guard let data = coder.decodeObject(forKey: "data") as! Data? else { return nil }
|
||||||
self.data = data
|
self.data = data
|
||||||
|
self.messageServerID = coder.decodeObject(forKey: "messageServerUD") as! UInt64?
|
||||||
self.failureCount = coder.decodeObject(forKey: "failureCount") as! UInt? ?? 0
|
self.failureCount = coder.decodeObject(forKey: "failureCount") as! UInt? ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(with coder: NSCoder) {
|
public func encode(with coder: NSCoder) {
|
||||||
coder.encode(data, forKey: "data")
|
coder.encode(data, forKey: "data")
|
||||||
|
coder.encode(messageServerID, forKey: "messageServerID")
|
||||||
coder.encode(failureCount, forKey: "failureCount")
|
coder.encode(failureCount, forKey: "failureCount")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +34,7 @@ public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NS
|
||||||
Configuration.shared.storage.with { transaction in // Intentionally capture self
|
Configuration.shared.storage.with { transaction in // Intentionally capture self
|
||||||
Threading.workQueue.async {
|
Threading.workQueue.async {
|
||||||
do {
|
do {
|
||||||
let _ = try MessageReceiver.parse(self.data, using: transaction)
|
let _ = try MessageReceiver.parse(self.data, messageServerID: self.messageServerID, using: transaction)
|
||||||
self.handleSuccess()
|
self.handleSuccess()
|
||||||
} catch {
|
} catch {
|
||||||
SNLog("Couldn't parse message due to error: \(error).")
|
SNLog("Couldn't parse message due to error: \(error).")
|
||||||
|
|
|
@ -30,7 +30,7 @@ internal enum MessageReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static func parse(_ data: Data, using transaction: Any) throws -> Message {
|
internal static func parse(_ data: Data, messageServerID: UInt64?, using transaction: Any) throws -> Message {
|
||||||
// Parse the envelope
|
// Parse the envelope
|
||||||
let envelope = try MessageWrapper.unwrap(data: data)
|
let envelope = try MessageWrapper.unwrap(data: data)
|
||||||
// Decrypt the contents
|
// Decrypt the contents
|
||||||
|
|
|
@ -880,7 +880,6 @@
|
||||||
C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F2255B6DBC007E1867 /* Searcher.swift */; };
|
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F2255B6DBC007E1867 /* Searcher.swift */; };
|
||||||
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */; };
|
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */; };
|
||||||
C38EF31E255B6DBF007E1867 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F4255B6DBC007E1867 /* ThreadUtil.m */; };
|
|
||||||
C38EF320255B6DBF007E1867 /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */; };
|
C38EF320255B6DBF007E1867 /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */; };
|
||||||
C38EF321255B6DBF007E1867 /* OWSAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */; };
|
C38EF321255B6DBF007E1867 /* OWSAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */; };
|
||||||
C38EF322255B6DBF007E1867 /* DebugLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F8255B6DBC007E1867 /* DebugLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C38EF322255B6DBF007E1867 /* DebugLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F8255B6DBC007E1867 /* DebugLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
@ -890,7 +889,6 @@
|
||||||
C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */; };
|
C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */; };
|
||||||
C38EF327255B6DBF007E1867 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */; };
|
C38EF327255B6DBF007E1867 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */; };
|
||||||
C38EF328255B6DBF007E1867 /* OWSContactAvatarBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C38EF328255B6DBF007E1867 /* OWSContactAvatarBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
C38EF329255B6DBF007E1867 /* ThreadUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FF255B6DBD007E1867 /* ThreadUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF300255B6DBD007E1867 /* UIUtil.m */; };
|
C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF300255B6DBD007E1867 /* UIUtil.m */; };
|
||||||
C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF301255B6DBD007E1867 /* OWSFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF301255B6DBD007E1867 /* OWSFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
C38EF32D255B6DBF007E1867 /* BlockListUIUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C38EF32D255B6DBF007E1867 /* BlockListUIUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
@ -2225,7 +2223,6 @@
|
||||||
C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSPreferences.h; path = SignalUtilitiesKit/OWSPreferences.h; sourceTree = SOURCE_ROOT; };
|
C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSPreferences.h; path = SignalUtilitiesKit/OWSPreferences.h; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Searcher.swift; path = SignalUtilitiesKit/Searcher.swift; sourceTree = SOURCE_ROOT; };
|
C38EF2F2255B6DBC007E1867 /* Searcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Searcher.swift; path = SignalUtilitiesKit/Searcher.swift; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+OWS.swift"; path = "SignalUtilitiesKit/UIImage+OWS.swift"; sourceTree = SOURCE_ROOT; };
|
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+OWS.swift"; path = "SignalUtilitiesKit/UIImage+OWS.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2F4255B6DBC007E1867 /* ThreadUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ThreadUtil.m; path = SignalUtilitiesKit/ThreadUtil.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAudioPlayer.h; path = SignalUtilitiesKit/OWSAudioPlayer.h; sourceTree = SOURCE_ROOT; };
|
C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAudioPlayer.h; path = SignalUtilitiesKit/OWSAudioPlayer.h; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSScrubbingLogFormatter.m; path = SignalUtilitiesKit/OWSScrubbingLogFormatter.m; sourceTree = SOURCE_ROOT; };
|
C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSScrubbingLogFormatter.m; path = SignalUtilitiesKit/OWSScrubbingLogFormatter.m; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAudioPlayer.m; path = SignalUtilitiesKit/OWSAudioPlayer.m; sourceTree = SOURCE_ROOT; };
|
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAudioPlayer.m; path = SignalUtilitiesKit/OWSAudioPlayer.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -2236,7 +2233,6 @@
|
||||||
C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationStyle.swift; path = SignalUtilitiesKit/ConversationStyle.swift; sourceTree = SOURCE_ROOT; };
|
C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationStyle.swift; path = SignalUtilitiesKit/ConversationStyle.swift; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlockListUIUtils.m; path = SignalUtilitiesKit/BlockListUIUtils.m; sourceTree = SOURCE_ROOT; };
|
C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlockListUIUtils.m; path = SignalUtilitiesKit/BlockListUIUtils.m; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSContactAvatarBuilder.h; path = SignalUtilitiesKit/OWSContactAvatarBuilder.h; sourceTree = SOURCE_ROOT; };
|
C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSContactAvatarBuilder.h; path = SignalUtilitiesKit/OWSContactAvatarBuilder.h; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF2FF255B6DBD007E1867 /* ThreadUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadUtil.h; path = SignalUtilitiesKit/ThreadUtil.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
C38EF300255B6DBD007E1867 /* UIUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIUtil.m; path = SignalUtilitiesKit/UIUtil.m; sourceTree = SOURCE_ROOT; };
|
C38EF300255B6DBD007E1867 /* UIUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIUtil.m; path = SignalUtilitiesKit/UIUtil.m; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF301255B6DBD007E1867 /* OWSFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFormat.h; path = SignalUtilitiesKit/OWSFormat.h; sourceTree = SOURCE_ROOT; };
|
C38EF301255B6DBD007E1867 /* OWSFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFormat.h; path = SignalUtilitiesKit/OWSFormat.h; sourceTree = SOURCE_ROOT; };
|
||||||
C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.h; sourceTree = SOURCE_ROOT; };
|
C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -3468,8 +3464,6 @@
|
||||||
C38EF306255B6DBE007E1867 /* OWSWindowManager.m */,
|
C38EF306255B6DBE007E1867 /* OWSWindowManager.m */,
|
||||||
C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */,
|
C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */,
|
||||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
||||||
C38EF2FF255B6DBD007E1867 /* ThreadUtil.h */,
|
|
||||||
C38EF2F4255B6DBC007E1867 /* ThreadUtil.m */,
|
|
||||||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
||||||
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */,
|
C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */,
|
||||||
C38EF30A255B6DBE007E1867 /* UIUtil.h */,
|
C38EF30A255B6DBE007E1867 /* UIUtil.h */,
|
||||||
|
@ -4562,7 +4556,6 @@
|
||||||
C33FDC6F255A582000E217F9 /* TSNetworkManager.h in Headers */,
|
C33FDC6F255A582000E217F9 /* TSNetworkManager.h in Headers */,
|
||||||
C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */,
|
C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */,
|
||||||
C33FDD19255A582000E217F9 /* YapDatabaseConnection+OWS.h in Headers */,
|
C33FDD19255A582000E217F9 /* YapDatabaseConnection+OWS.h in Headers */,
|
||||||
C38EF329255B6DBF007E1867 /* ThreadUtil.h in Headers */,
|
|
||||||
C33FDD1F255A582000E217F9 /* OWSSyncConfigurationMessage.h in Headers */,
|
C33FDD1F255A582000E217F9 /* OWSSyncConfigurationMessage.h in Headers */,
|
||||||
C38EF216255B6D3B007E1867 /* Theme.h in Headers */,
|
C38EF216255B6D3B007E1867 /* Theme.h in Headers */,
|
||||||
C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */,
|
C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */,
|
||||||
|
@ -5827,7 +5820,6 @@
|
||||||
C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */,
|
C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */,
|
||||||
C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */,
|
C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */,
|
||||||
C33FDC5E255A582000E217F9 /* SSKProto.swift in Sources */,
|
C33FDC5E255A582000E217F9 /* SSKProto.swift in Sources */,
|
||||||
C38EF31E255B6DBF007E1867 /* ThreadUtil.m in Sources */,
|
|
||||||
C38EF39E255B6DDA007E1867 /* OWSQuotedReplyModel.m in Sources */,
|
C38EF39E255B6DDA007E1867 /* OWSQuotedReplyModel.m in Sources */,
|
||||||
C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */,
|
C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */,
|
||||||
C33FDD2E255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.m in Sources */,
|
C33FDD2E255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.m in Sources */,
|
||||||
|
|
|
@ -216,7 +216,7 @@ public class FullTextSearchFinder: NSObject {
|
||||||
if let groupThread = object as? TSGroupThread {
|
if let groupThread = object as? TSGroupThread {
|
||||||
return self.groupThreadIndexer.index(groupThread, transaction: transaction)
|
return self.groupThreadIndexer.index(groupThread, transaction: transaction)
|
||||||
} else if let contactThread = object as? TSContactThread {
|
} else if let contactThread = object as? TSContactThread {
|
||||||
guard contactThread.shouldThreadBeVisible && !contactThread.isSlaveThread else {
|
guard contactThread.shouldThreadBeVisible else {
|
||||||
// If we've never sent/received a message in a TSContactThread,
|
// If we've never sent/received a message in a TSContactThread,
|
||||||
// then we want it to appear in the "Other Contacts" section rather
|
// then we want it to appear in the "Other Contacts" section rather
|
||||||
// than in the "Conversations" section.
|
// than in the "Conversations" section.
|
||||||
|
|
|
@ -102,7 +102,6 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
|
||||||
#import <SignalUtilitiesKit/SSKEnvironment.h>
|
#import <SignalUtilitiesKit/SSKEnvironment.h>
|
||||||
#import <SignalUtilitiesKit/SSKMessageSenderJobRecord.h>
|
#import <SignalUtilitiesKit/SSKMessageSenderJobRecord.h>
|
||||||
#import <SignalUtilitiesKit/Theme.h>
|
#import <SignalUtilitiesKit/Theme.h>
|
||||||
#import <SignalUtilitiesKit/ThreadUtil.h>
|
|
||||||
#import <SignalUtilitiesKit/TSAttachmentPointer.h>
|
#import <SignalUtilitiesKit/TSAttachmentPointer.h>
|
||||||
#import <SignalUtilitiesKit/TSAttachmentStream.h>
|
#import <SignalUtilitiesKit/TSAttachmentStream.h>
|
||||||
#import <SignalUtilitiesKit/TSCall.h>
|
#import <SignalUtilitiesKit/TSCall.h>
|
||||||
|
|
|
@ -178,7 +178,10 @@ public final class PublicChatPoller : NSObject {
|
||||||
Storage.writeSync { transaction in
|
Storage.writeSync { transaction in
|
||||||
transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: publicChat.id)
|
transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: publicChat.id)
|
||||||
let messageServerID = message.serverID
|
let messageServerID = message.serverID
|
||||||
SSKEnvironment.shared.messageManager.throws_processEnvelope(try! envelope.build(), plaintextData: try! content.build().serializedData(), wasReceivedByUD: false, transaction: transaction, serverID: messageServerID ?? 0)
|
let job = MessageReceiveJob(data: try! envelope.buildSerializedData(), messageServerID: messageServerID)
|
||||||
|
Storage.write { transaction in
|
||||||
|
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
|
||||||
|
}
|
||||||
// If we got a message from our master device then we should use its profile picture
|
// If we got a message from our master device then we should use its profile picture
|
||||||
if let profilePicture = message.profilePicture, masterPublicKey == message.senderPublicKey {
|
if let profilePicture = message.profilePicture, masterPublicKey == message.senderPublicKey {
|
||||||
if (message.displayName.count > 0) {
|
if (message.displayName.count > 0) {
|
||||||
|
|
|
@ -4,14 +4,12 @@
|
||||||
|
|
||||||
#import "SharingThreadPickerViewController.h"
|
#import "SharingThreadPickerViewController.h"
|
||||||
#import "SignalApp.h"
|
#import "SignalApp.h"
|
||||||
#import "ThreadUtil.h"
|
|
||||||
#import "UIColor+OWS.h"
|
#import "UIColor+OWS.h"
|
||||||
#import "UIFont+OWS.h"
|
#import "UIFont+OWS.h"
|
||||||
#import "UIView+OWS.h"
|
#import "UIView+OWS.h"
|
||||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||||
#import <SignalUtilitiesKit/NSString+SSK.h>
|
#import <SignalUtilitiesKit/NSString+SSK.h>
|
||||||
#import <SignalUtilitiesKit/OWSError.h>
|
#import <SignalUtilitiesKit/OWSError.h>
|
||||||
#import <SignalUtilitiesKit/OWSMessageSender.h>
|
|
||||||
#import <SignalUtilitiesKit/TSThread.h>
|
#import <SignalUtilitiesKit/TSThread.h>
|
||||||
#import <SessionUIKit/SessionUIKit.h>
|
#import <SessionUIKit/SessionUIKit.h>
|
||||||
|
|
||||||
|
|
|
@ -244,15 +244,15 @@ public class SignalAttachment: NSObject {
|
||||||
return errorDescription
|
return errorDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
// @objc
|
||||||
public func buildOutgoingAttachmentInfo(message: TSMessage) -> OutgoingAttachmentInfo {
|
// public func buildOutgoingAttachmentInfo(message: TSMessage) -> OutgoingAttachmentInfo {
|
||||||
assert(message.uniqueId != nil)
|
// assert(message.uniqueId != nil)
|
||||||
return OutgoingAttachmentInfo(dataSource: dataSource,
|
// return OutgoingAttachmentInfo(dataSource: dataSource,
|
||||||
contentType: mimeType,
|
// contentType: mimeType,
|
||||||
sourceFilename: filenameOrDefault,
|
// sourceFilename: filenameOrDefault,
|
||||||
caption: captionText,
|
// caption: captionText,
|
||||||
albumMessageId: message.uniqueId)
|
// albumMessageId: message.uniqueId)
|
||||||
}
|
// }
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public func staticThumbnail() -> UIImage? {
|
public func staticThumbnail() -> UIImage? {
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
||||||
#import "OWSFingerprint.h"
|
#import "OWSFingerprint.h"
|
||||||
#import "OWSIdentityManager.h"
|
#import "OWSIdentityManager.h"
|
||||||
#import "OWSMessageManager.h"
|
|
||||||
#import "OWSMessageReceiver.h"
|
|
||||||
#import "OWSPrimaryStorage+SessionStore.h"
|
#import "OWSPrimaryStorage+SessionStore.h"
|
||||||
#import "OWSPrimaryStorage.h"
|
#import "OWSPrimaryStorage.h"
|
||||||
#import "SSKEnvironment.h"
|
#import "SSKEnvironment.h"
|
||||||
|
@ -108,7 +106,6 @@ __attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage
|
||||||
[self.thread receivedMessagesForInvalidKey:newKey];
|
[self.thread receivedMessagesForInvalidKey:newKey];
|
||||||
|
|
||||||
for (TSInvalidIdentityKeyReceivingErrorMessage *errorMessage in messagesToDecrypt) {
|
for (TSInvalidIdentityKeyReceivingErrorMessage *errorMessage in messagesToDecrypt) {
|
||||||
[SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:errorMessage.envelopeData];
|
|
||||||
|
|
||||||
// Here we remove the existing error message because handleReceivedEnvelope will either
|
// Here we remove the existing error message because handleReceivedEnvelope will either
|
||||||
// 1.) succeed and create a new successful message in the thread or...
|
// 1.) succeed and create a new successful message in the thread or...
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#import "TSOutgoingMessage.h"
|
#import "TSOutgoingMessage.h"
|
||||||
#import "NSString+SSK.h"
|
#import "NSString+SSK.h"
|
||||||
#import "OWSContact.h"
|
#import "OWSContact.h"
|
||||||
#import "OWSMessageSender.h"
|
|
||||||
#import "OWSOutgoingSyncMessage.h"
|
#import "OWSOutgoingSyncMessage.h"
|
||||||
#import "OWSPrimaryStorage.h"
|
#import "OWSPrimaryStorage.h"
|
||||||
#import "ProfileManagerProtocol.h"
|
#import "ProfileManagerProtocol.h"
|
||||||
|
|
|
@ -38,7 +38,6 @@ extern ConversationColorName const kConversationColorName_Default;
|
||||||
@property (nonatomic, readonly) NSDate *creationDate;
|
@property (nonatomic, readonly) NSDate *creationDate;
|
||||||
@property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting;
|
@property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting;
|
||||||
@property (nonatomic, readonly) TSInteraction *lastInteraction;
|
@property (nonatomic, readonly) TSInteraction *lastInteraction;
|
||||||
@property (nonatomic, readonly) BOOL isSlaveThread;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the object is a group thread or not.
|
* Whether the object is a group thread or not.
|
||||||
|
|
|
@ -730,11 +730,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isSlaveThread
|
|
||||||
{
|
|
||||||
return [LKMultiDeviceProtocol isSlaveThread:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@class LKDeviceLinkMessage;
|
|
||||||
@class LKUnlinkDeviceMessage;
|
|
||||||
@class OWSBlockingManager;
|
|
||||||
@class OWSContact;
|
|
||||||
@class OWSContactsManager;
|
|
||||||
@class OWSLinkPreviewDraft;
|
|
||||||
@class OWSMessageSender;
|
|
||||||
@class OWSQuotedReplyModel;
|
|
||||||
@class OWSUnreadIndicator;
|
|
||||||
@class SignalAttachment;
|
|
||||||
@class TSContactThread;
|
|
||||||
@class TSGroupThread;
|
|
||||||
@class TSInteraction;
|
|
||||||
@class TSOutgoingMessage;
|
|
||||||
@class TSThread;
|
|
||||||
@class YapDatabaseConnection;
|
|
||||||
@class YapDatabaseReadTransaction;
|
|
||||||
@class YapDatabaseReadWriteTransaction;
|
|
||||||
|
|
||||||
@interface ThreadDynamicInteractions : NSObject
|
|
||||||
|
|
||||||
// Represents the "reverse index" of the focus message, if any.
|
|
||||||
// The "reverse index" is the distance of this interaction from
|
|
||||||
// the last interaction in the thread. Therefore the last interaction
|
|
||||||
// will have a "reverse index" of zero.
|
|
||||||
//
|
|
||||||
// We use "reverse indices" because (among other uses) we use this to
|
|
||||||
// determine the initial load window size.
|
|
||||||
@property (nonatomic, nullable, readonly) NSNumber *focusMessagePosition;
|
|
||||||
|
|
||||||
@property (nonatomic, nullable, readonly) OWSUnreadIndicator *unreadIndicator;
|
|
||||||
|
|
||||||
- (void)clearUnreadIndicatorState;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@interface ThreadUtil : NSObject
|
|
||||||
|
|
||||||
#pragma mark - Durable Message Enqueue
|
|
||||||
|
|
||||||
+ (void)enqueueDeviceLinkMessage:(LKDeviceLinkMessage *)message;
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithText:(nullable NSString *)fullMessageText
|
|
||||||
mediaAttachments:(NSArray<SignalAttachment *> *)attachments
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithContactShare:(OWSContact *)contactShare inThread:(TSThread *)thread;
|
|
||||||
+ (void)enqueueLeaveGroupMessageInThread:(TSGroupThread *)thread;
|
|
||||||
|
|
||||||
#pragma mark - Non-Durable Sending
|
|
||||||
|
|
||||||
// Used by SAE and "reply from lockscreen", otherwise we should use the durable `enqueue` counterpart
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithText:(NSString *)fullMessageText
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion;
|
|
||||||
|
|
||||||
// Used by SAE, otherwise we should use the durable `enqueue` counterpart
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithText:(NSString *)fullMessageText
|
|
||||||
mediaAttachments:(NSArray<SignalAttachment *> *)attachments
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion;
|
|
||||||
|
|
||||||
// Used by SAE, otherwise we should use the durable `enqueue` counterpart
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithContactShare:(OWSContact *)contactShare
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - dynamic interactions
|
|
||||||
|
|
||||||
// This method will create and/or remove any offers and indicators
|
|
||||||
// necessary for this thread. This includes:
|
|
||||||
//
|
|
||||||
// * Block offers.
|
|
||||||
// * "Add to contacts" offers.
|
|
||||||
// * Unread indicators.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
//
|
|
||||||
// * hideUnreadMessagesIndicator: If YES, the "unread indicator" has
|
|
||||||
// been cleared and should not be shown.
|
|
||||||
// * firstUnseenInteractionTimestamp: A snapshot of unseen message state
|
|
||||||
// when we entered the conversation view. See comments on
|
|
||||||
// ThreadOffersAndIndicators.
|
|
||||||
// * maxRangeSize: Loading a lot of messages in conversation view is
|
|
||||||
// slow and unwieldy. This number represents the maximum current
|
|
||||||
// size of the "load window" in that view. The unread indicator should
|
|
||||||
// always be inserted within that window.
|
|
||||||
+ (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread
|
|
||||||
contactsManager:(OWSContactsManager *)contactsManager
|
|
||||||
blockingManager:(OWSBlockingManager *)blockingManager
|
|
||||||
dbConnection:(YapDatabaseConnection *)dbConnection
|
|
||||||
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
|
|
||||||
lastUnreadIndicator:(nullable OWSUnreadIndicator *)lastUnreadIndicator
|
|
||||||
focusMessageId:(nullable NSString *)focusMessageId
|
|
||||||
maxRangeSize:(int)maxRangeSize;
|
|
||||||
|
|
||||||
+ (BOOL)shouldShowGroupProfileBannerInThread:(TSThread *)thread blockingManager:(OWSBlockingManager *)blockingManager;
|
|
||||||
|
|
||||||
// This method should be called right _before_ we send a message to a thread,
|
|
||||||
// since we want to auto-add contact threads to the profile whitelist if the
|
|
||||||
// conversation was initiated by the local user.
|
|
||||||
//
|
|
||||||
// Returns YES IFF the thread was just added to the profile whitelist.
|
|
||||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyContactThread:(TSThread *)thread;
|
|
||||||
|
|
||||||
#pragma mark - Delete Content
|
|
||||||
|
|
||||||
+ (void)deleteAllContent;
|
|
||||||
|
|
||||||
#pragma mark - Find Content
|
|
||||||
|
|
||||||
+ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp
|
|
||||||
authorId:(NSString *)authorId
|
|
||||||
threadUniqueId:(NSString *)threadUniqueId
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
|
@ -1,797 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ThreadUtil.h"
|
|
||||||
#import "OWSContactOffersInteraction.h"
|
|
||||||
#import "OWSContactsManager.h"
|
|
||||||
#import "OWSQuotedReplyModel.h"
|
|
||||||
#import "OWSUnreadIndicator.h"
|
|
||||||
#import "TSUnreadIndicatorInteraction.h"
|
|
||||||
#import <SignalUtilitiesKit/OWSProfileManager.h>
|
|
||||||
#import <SignalUtilitiesKit/SSKEnvironment.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSAddToContactsOfferMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSBlockingManager.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSDisappearingMessagesConfiguration.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSMessageSender.h>
|
|
||||||
#import <SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/TSAccountManager.h>
|
|
||||||
#import <SignalUtilitiesKit/TSCall.h>
|
|
||||||
#import <SignalUtilitiesKit/TSContactThread.h>
|
|
||||||
#import <SignalUtilitiesKit/TSDatabaseView.h>
|
|
||||||
#import <SignalUtilitiesKit/TSIncomingMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/TSInvalidIdentityKeyErrorMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/TSOutgoingMessage.h>
|
|
||||||
#import <SignalUtilitiesKit/TSThread.h>
|
|
||||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
|
||||||
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface ThreadDynamicInteractions ()
|
|
||||||
|
|
||||||
@property (nonatomic, nullable) NSNumber *focusMessagePosition;
|
|
||||||
|
|
||||||
@property (nonatomic, nullable) OWSUnreadIndicator *unreadIndicator;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation ThreadDynamicInteractions
|
|
||||||
|
|
||||||
- (void)clearUnreadIndicatorState
|
|
||||||
{
|
|
||||||
self.unreadIndicator = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)object
|
|
||||||
{
|
|
||||||
if (self == object) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![object isKindOfClass:[ThreadDynamicInteractions class]]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadDynamicInteractions *other = (ThreadDynamicInteractions *)object;
|
|
||||||
return ([NSObject isNullableObject:self.focusMessagePosition equalTo:other.focusMessagePosition] &&
|
|
||||||
[NSObject isNullableObject:self.unreadIndicator equalTo:other.unreadIndicator]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMessage,
|
|
||||||
NSMutableArray<OWSOutgoingAttachmentInfo *> *attachmentInfos,
|
|
||||||
YapDatabaseReadWriteTransaction *writeTransaction);
|
|
||||||
|
|
||||||
@implementation ThreadUtil
|
|
||||||
|
|
||||||
#pragma mark - Dependencies
|
|
||||||
|
|
||||||
+ (SSKMessageSenderJobQueue *)messageSenderJobQueue
|
|
||||||
{
|
|
||||||
return SSKEnvironment.shared.messageSenderJobQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (YapDatabaseConnection *)dbConnection
|
|
||||||
{
|
|
||||||
return SSKEnvironment.shared.primaryStorage.dbReadWriteConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Durable Message Enqueue
|
|
||||||
|
|
||||||
+ (void)enqueueDeviceLinkMessage:(LKDeviceLinkMessage *)message
|
|
||||||
{
|
|
||||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
||||||
[self.messageSenderJobQueue addMessage:message transaction:transaction];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
{
|
|
||||||
return [self enqueueMessageWithText:fullMessageText
|
|
||||||
mediaAttachments:@[]
|
|
||||||
inThread:thread
|
|
||||||
quotedReplyModel:quotedReplyModel
|
|
||||||
linkPreviewDraft:linkPreviewDraft
|
|
||||||
transaction:transaction];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithText:(nullable NSString *)fullMessageText
|
|
||||||
mediaAttachments:(NSArray<SignalAttachment *> *)mediaAttachments
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
linkPreviewDraft:(nullable nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
|
|
||||||
return [self
|
|
||||||
buildOutgoingMessageWithText:fullMessageText
|
|
||||||
mediaAttachments:mediaAttachments
|
|
||||||
thread:thread
|
|
||||||
quotedReplyModel:quotedReplyModel
|
|
||||||
linkPreviewDraft:linkPreviewDraft
|
|
||||||
transaction:transaction
|
|
||||||
completion:^(TSOutgoingMessage *savedMessage,
|
|
||||||
NSMutableArray<OWSOutgoingAttachmentInfo *> *attachmentInfos,
|
|
||||||
YapDatabaseReadWriteTransaction *writeTransaction) {
|
|
||||||
if (attachmentInfos.count == 0) {
|
|
||||||
[self.messageSenderJobQueue addMessage:savedMessage transaction:writeTransaction];
|
|
||||||
} else {
|
|
||||||
[self.messageSenderJobQueue addMediaMessage:savedMessage
|
|
||||||
attachmentInfos:attachmentInfos
|
|
||||||
isTemporaryAttachment:NO];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)buildOutgoingMessageWithText:(nullable NSString *)fullMessageText
|
|
||||||
mediaAttachments:(NSArray<SignalAttachment *> *)mediaAttachments
|
|
||||||
thread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
linkPreviewDraft:(nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
completion:(BuildOutgoingMessageCompletionBlock)completionBlock;
|
|
||||||
{
|
|
||||||
NSString *_Nullable truncatedText;
|
|
||||||
NSArray<SignalAttachment *> *attachments = mediaAttachments;
|
|
||||||
if ([fullMessageText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] <= kOversizeTextMessageSizeThreshold) {
|
|
||||||
truncatedText = fullMessageText;
|
|
||||||
} else {
|
|
||||||
if (SSKFeatureFlags.sendingMediaWithOversizeText) {
|
|
||||||
truncatedText = [fullMessageText ows_truncatedToByteCount:kOversizeTextMessageSizeThreshold];
|
|
||||||
} else {
|
|
||||||
// Legacy iOS clients already support receiving long text, but they assume _any_ body
|
|
||||||
// text is the _full_ body text. So until we consider "rollout" complete, we maintain
|
|
||||||
// the legacy sending behavior, which does not include the truncated text in the
|
|
||||||
// websocket proto.
|
|
||||||
truncatedText = nil;
|
|
||||||
}
|
|
||||||
DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:fullMessageText];
|
|
||||||
if (dataSource) {
|
|
||||||
SignalAttachment *oversizeTextAttachment =
|
|
||||||
[SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI];
|
|
||||||
attachments = [mediaAttachments arrayByAddingObject:oversizeTextAttachment];
|
|
||||||
} else {
|
|
||||||
OWSFailDebug(@"dataSource was unexpectedly nil.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OWSDisappearingMessagesConfiguration *configuration =
|
|
||||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction];
|
|
||||||
|
|
||||||
uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
|
|
||||||
|
|
||||||
for (SignalAttachment *attachment in attachments) {
|
|
||||||
OWSAssertDebug(!attachment.hasError);
|
|
||||||
OWSAssertDebug(attachment.mimeType.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL isVoiceMessage = (attachments.count == 1 && attachments.lastObject.isVoiceMessage);
|
|
||||||
|
|
||||||
TSOutgoingMessage *message =
|
|
||||||
[[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
|
||||||
inThread:thread
|
|
||||||
messageBody:truncatedText
|
|
||||||
attachmentIds:[NSMutableArray new]
|
|
||||||
expiresInSeconds:expiresInSeconds
|
|
||||||
expireStartedAt:0
|
|
||||||
isVoiceMessage:isVoiceMessage
|
|
||||||
groupMetaMessage:TSGroupMetaMessageUnspecified
|
|
||||||
quotedMessage:[quotedReplyModel buildQuotedMessageForSending]
|
|
||||||
contactShare:nil
|
|
||||||
linkPreview:nil];
|
|
||||||
[message save];
|
|
||||||
|
|
||||||
[BenchManager
|
|
||||||
benchAsyncWithTitle:@"Saving outgoing message"
|
|
||||||
block:^(void (^benchmarkCompletion)(void)) {
|
|
||||||
// To avoid blocking the send flow, we dispatch an async write from within this read
|
|
||||||
// transaction
|
|
||||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull writeTransaction) {
|
|
||||||
|
|
||||||
OWSLinkPreview *_Nullable linkPreview =
|
|
||||||
[self linkPreviewForLinkPreviewDraft:linkPreviewDraft
|
|
||||||
transaction:writeTransaction];
|
|
||||||
if (linkPreview) {
|
|
||||||
[message updateWithLinkPreview:linkPreview transaction:writeTransaction];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray<OWSOutgoingAttachmentInfo *> *attachmentInfos = [NSMutableArray new];
|
|
||||||
for (SignalAttachment *attachment in attachments) {
|
|
||||||
OWSOutgoingAttachmentInfo *attachmentInfo = [attachment buildOutgoingAttachmentInfoWithMessage:message];
|
|
||||||
[attachmentInfos addObject:attachmentInfo];
|
|
||||||
}
|
|
||||||
completionBlock(message, attachmentInfos, writeTransaction);
|
|
||||||
} completion:benchmarkCompletion];
|
|
||||||
}];
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)enqueueMessageWithContactShare:(OWSContact *)contactShare inThread:(TSThread *)thread;
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(contactShare);
|
|
||||||
OWSAssertDebug(contactShare.ows_isValid);
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
|
|
||||||
OWSDisappearingMessagesConfiguration *configuration =
|
|
||||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId];
|
|
||||||
|
|
||||||
uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
|
|
||||||
TSOutgoingMessage *message =
|
|
||||||
[[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
|
||||||
inThread:thread
|
|
||||||
messageBody:nil
|
|
||||||
attachmentIds:[NSMutableArray new]
|
|
||||||
expiresInSeconds:expiresInSeconds
|
|
||||||
expireStartedAt:0
|
|
||||||
isVoiceMessage:NO
|
|
||||||
groupMetaMessage:TSGroupMetaMessageUnspecified
|
|
||||||
quotedMessage:nil
|
|
||||||
contactShare:contactShare
|
|
||||||
linkPreview:nil];
|
|
||||||
|
|
||||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
|
||||||
[message saveWithTransaction:transaction];
|
|
||||||
[self.messageSenderJobQueue addMessage:message transaction:transaction];
|
|
||||||
}];
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)enqueueLeaveGroupMessageInThread:(TSGroupThread *)thread
|
|
||||||
{
|
|
||||||
OWSAssertDebug([thread isKindOfClass:[TSGroupThread class]]);
|
|
||||||
|
|
||||||
TSOutgoingMessage *message =
|
|
||||||
[TSOutgoingMessage outgoingMessageInThread:thread groupMetaMessage:TSGroupMetaMessageQuit expiresInSeconds:0];
|
|
||||||
|
|
||||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
|
||||||
[self.messageSenderJobQueue addMessage:message transaction:transaction];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Non-Durable Sending
|
|
||||||
|
|
||||||
// We might want to generate a link preview here.
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithText:(NSString *)fullMessageText
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion
|
|
||||||
{
|
|
||||||
OWSAssertDebug(completion);
|
|
||||||
|
|
||||||
return [self sendMessageNonDurablyWithText:fullMessageText
|
|
||||||
mediaAttachments:@[]
|
|
||||||
inThread:thread
|
|
||||||
quotedReplyModel:quotedReplyModel
|
|
||||||
transaction:transaction
|
|
||||||
messageSender:messageSender
|
|
||||||
completion:completion];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithText:(NSString *)fullMessageText
|
|
||||||
mediaAttachments:(NSArray<SignalAttachment *> *)mediaAttachments
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(completion);
|
|
||||||
|
|
||||||
return
|
|
||||||
[self buildOutgoingMessageWithText:fullMessageText
|
|
||||||
mediaAttachments:mediaAttachments
|
|
||||||
thread:thread
|
|
||||||
quotedReplyModel:quotedReplyModel
|
|
||||||
linkPreviewDraft:nil
|
|
||||||
transaction:transaction
|
|
||||||
completion:^(TSOutgoingMessage *_Nonnull savedMessage,
|
|
||||||
NSMutableArray<OWSOutgoingAttachmentInfo *> *_Nonnull attachmentInfos,
|
|
||||||
YapDatabaseReadWriteTransaction *_Nonnull writeTransaction) {
|
|
||||||
if (attachmentInfos.count == 0) {
|
|
||||||
[messageSender sendMessage:savedMessage
|
|
||||||
success:^{
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(nil);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(error);
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
[messageSender sendAttachments:attachmentInfos
|
|
||||||
inMessage:savedMessage
|
|
||||||
success:^{
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(nil);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(error);
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (TSOutgoingMessage *)sendMessageNonDurablyWithContactShare:(OWSContact *)contactShare
|
|
||||||
inThread:(TSThread *)thread
|
|
||||||
messageSender:(OWSMessageSender *)messageSender
|
|
||||||
completion:(void (^)(NSError *_Nullable error))completion
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(contactShare);
|
|
||||||
OWSAssertDebug(contactShare.ows_isValid);
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(messageSender);
|
|
||||||
OWSAssertDebug(completion);
|
|
||||||
|
|
||||||
OWSDisappearingMessagesConfiguration *configuration =
|
|
||||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId];
|
|
||||||
|
|
||||||
uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
|
|
||||||
// MJK TODO - remove senderTimestamp
|
|
||||||
TSOutgoingMessage *message =
|
|
||||||
[[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
|
||||||
inThread:thread
|
|
||||||
messageBody:nil
|
|
||||||
attachmentIds:[NSMutableArray new]
|
|
||||||
expiresInSeconds:expiresInSeconds
|
|
||||||
expireStartedAt:0
|
|
||||||
isVoiceMessage:NO
|
|
||||||
groupMetaMessage:TSGroupMetaMessageUnspecified
|
|
||||||
quotedMessage:nil
|
|
||||||
contactShare:contactShare
|
|
||||||
linkPreview:nil];
|
|
||||||
|
|
||||||
[messageSender sendMessage:message
|
|
||||||
success:^{
|
|
||||||
OWSLogDebug(@"Successfully sent contact share.");
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(nil);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
OWSLogError(@"Failed to send contact share with error: %@", error);
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
completion(error);
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (nullable OWSLinkPreview *)linkPreviewForLinkPreviewDraft:(nullable OWSLinkPreviewDraft *)linkPreviewDraft
|
|
||||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
||||||
{
|
|
||||||
OWSAssertDebug(transaction);
|
|
||||||
|
|
||||||
if (!linkPreviewDraft) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSError *linkPreviewError;
|
|
||||||
OWSLinkPreview *_Nullable linkPreview = [OWSLinkPreview buildValidatedLinkPreviewFromInfo:linkPreviewDraft
|
|
||||||
transaction:transaction
|
|
||||||
error:&linkPreviewError];
|
|
||||||
if (linkPreviewError && ![OWSLinkPreview isNoPreviewError:linkPreviewError]) {
|
|
||||||
OWSLogError(@"linkPreviewError: %@", linkPreviewError);
|
|
||||||
}
|
|
||||||
return linkPreview;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Dynamic Interactions
|
|
||||||
|
|
||||||
+ (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread
|
|
||||||
contactsManager:(OWSContactsManager *)contactsManager
|
|
||||||
blockingManager:(OWSBlockingManager *)blockingManager
|
|
||||||
dbConnection:(YapDatabaseConnection *)dbConnection
|
|
||||||
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
|
|
||||||
lastUnreadIndicator:(nullable OWSUnreadIndicator *)lastUnreadIndicator
|
|
||||||
focusMessageId:(nullable NSString *)focusMessageId
|
|
||||||
maxRangeSize:(int)maxRangeSize
|
|
||||||
{
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(dbConnection);
|
|
||||||
OWSAssertDebug(contactsManager);
|
|
||||||
OWSAssertDebug(blockingManager);
|
|
||||||
OWSAssertDebug(maxRangeSize > 0);
|
|
||||||
|
|
||||||
ThreadDynamicInteractions *result = [ThreadDynamicInteractions new];
|
|
||||||
|
|
||||||
[dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
||||||
// Find any "dynamic" interactions and safety number changes.
|
|
||||||
//
|
|
||||||
// We use different views for performance reasons.
|
|
||||||
NSMutableArray<TSInvalidIdentityKeyErrorMessage *> *blockingSafetyNumberChanges = [NSMutableArray new];
|
|
||||||
NSMutableArray<TSInteraction *> *nonBlockingSafetyNumberChanges = [NSMutableArray new];
|
|
||||||
[[TSDatabaseView threadSpecialMessagesDatabaseView:transaction]
|
|
||||||
enumerateKeysAndObjectsInGroup:thread.uniqueId
|
|
||||||
usingBlock:^(
|
|
||||||
NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
|
|
||||||
if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
|
|
||||||
[blockingSafetyNumberChanges addObject:object];
|
|
||||||
} else if ([object isKindOfClass:[TSErrorMessage class]]) {
|
|
||||||
TSErrorMessage *errorMessage = (TSErrorMessage *)object;
|
|
||||||
OWSAssertDebug(
|
|
||||||
errorMessage.errorType == TSErrorMessageNonBlockingIdentityChange);
|
|
||||||
[nonBlockingSafetyNumberChanges addObject:errorMessage];
|
|
||||||
} else {
|
|
||||||
OWSFailDebug(@"Unexpected dynamic interaction type: %@", [object class]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Determine if there are "unread" messages in this conversation.
|
|
||||||
// If we've been passed a firstUnseenInteractionTimestampParameter,
|
|
||||||
// just use that value in order to preserve continuity of the
|
|
||||||
// unread messages indicator after all messages in the conversation
|
|
||||||
// have been marked as read.
|
|
||||||
//
|
|
||||||
// IFF this variable is non-null, there are unseen messages in the thread.
|
|
||||||
NSNumber *_Nullable firstUnseenSortId = nil;
|
|
||||||
if (lastUnreadIndicator) {
|
|
||||||
firstUnseenSortId = @(lastUnreadIndicator.firstUnseenSortId);
|
|
||||||
} else {
|
|
||||||
TSInteraction *_Nullable firstUnseenInteraction =
|
|
||||||
[[TSDatabaseView unseenDatabaseViewExtension:transaction] firstObjectInGroup:thread.uniqueId];
|
|
||||||
if (firstUnseenInteraction && firstUnseenInteraction.sortId != NULL) {
|
|
||||||
firstUnseenSortId = @(firstUnseenInteraction.sortId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[self ensureUnreadIndicator:result
|
|
||||||
thread:thread
|
|
||||||
transaction:transaction
|
|
||||||
maxRangeSize:maxRangeSize
|
|
||||||
blockingSafetyNumberChanges:blockingSafetyNumberChanges
|
|
||||||
nonBlockingSafetyNumberChanges:nonBlockingSafetyNumberChanges
|
|
||||||
hideUnreadMessagesIndicator:hideUnreadMessagesIndicator
|
|
||||||
firstUnseenSortId:firstUnseenSortId];
|
|
||||||
|
|
||||||
// Determine the position of the focus message _after_ performing any mutations
|
|
||||||
// around dynamic interactions.
|
|
||||||
if (focusMessageId != nil) {
|
|
||||||
result.focusMessagePosition =
|
|
||||||
[self focusMessagePositionForThread:thread transaction:transaction focusMessageId:focusMessageId];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)ensureUnreadIndicator:(ThreadDynamicInteractions *)dynamicInteractions
|
|
||||||
thread:(TSThread *)thread
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
maxRangeSize:(int)maxRangeSize
|
|
||||||
blockingSafetyNumberChanges:(NSArray<TSInvalidIdentityKeyErrorMessage *> *)blockingSafetyNumberChanges
|
|
||||||
nonBlockingSafetyNumberChanges:(NSArray<TSInteraction *> *)nonBlockingSafetyNumberChanges
|
|
||||||
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
|
|
||||||
firstUnseenSortId:(nullable NSNumber *)firstUnseenSortId
|
|
||||||
{
|
|
||||||
OWSAssertDebug(dynamicInteractions);
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(transaction);
|
|
||||||
OWSAssertDebug(blockingSafetyNumberChanges);
|
|
||||||
OWSAssertDebug(nonBlockingSafetyNumberChanges);
|
|
||||||
|
|
||||||
if (hideUnreadMessagesIndicator) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!firstUnseenSortId) {
|
|
||||||
// If there are no unseen interactions, don't show an unread indicator.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
YapDatabaseViewTransaction *threadMessagesTransaction = [transaction ext:TSMessageDatabaseViewExtensionName];
|
|
||||||
OWSAssertDebug([threadMessagesTransaction isKindOfClass:[YapDatabaseViewTransaction class]]);
|
|
||||||
|
|
||||||
// Determine unread indicator position, if necessary.
|
|
||||||
//
|
|
||||||
// Enumerate in reverse to count the number of messages
|
|
||||||
// after the unseen messages indicator. Not all of
|
|
||||||
// them are unnecessarily unread, but we need to tell
|
|
||||||
// the messages view the position of the unread indicator,
|
|
||||||
// so that it can widen its "load window" to always show
|
|
||||||
// the unread indicator.
|
|
||||||
__block long visibleUnseenMessageCount = 0;
|
|
||||||
__block TSInteraction *interactionAfterUnreadIndicator = nil;
|
|
||||||
__block BOOL hasMoreUnseenMessages = NO;
|
|
||||||
[threadMessagesTransaction
|
|
||||||
enumerateKeysAndObjectsInGroup:thread.uniqueId
|
|
||||||
withOptions:NSEnumerationReverse
|
|
||||||
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
|
|
||||||
if (![object isKindOfClass:[TSInteraction class]]) {
|
|
||||||
OWSFailDebug(@"Expected a TSInteraction: %@", [object class]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TSInteraction *interaction = (TSInteraction *)object;
|
|
||||||
|
|
||||||
if (interaction.isDynamicInteraction) {
|
|
||||||
// Ignore dynamic interactions, if any.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interaction.sortId < firstUnseenSortId.unsignedLongLongValue) {
|
|
||||||
// By default we want the unread indicator to appear just before
|
|
||||||
// the first unread message.
|
|
||||||
*stop = YES;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
visibleUnseenMessageCount++;
|
|
||||||
|
|
||||||
interactionAfterUnreadIndicator = interaction;
|
|
||||||
|
|
||||||
if (visibleUnseenMessageCount + 1 >= maxRangeSize) {
|
|
||||||
// If there are more unseen messages than can be displayed in the
|
|
||||||
// messages view, show the unread indicator at the top of the
|
|
||||||
// displayed messages.
|
|
||||||
*stop = YES;
|
|
||||||
hasMoreUnseenMessages = YES;
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
if (!interactionAfterUnreadIndicator) {
|
|
||||||
// If we can't find an interaction after the unread indicator,
|
|
||||||
// don't show it. All unread messages may have been deleted or
|
|
||||||
// expired.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OWSAssertDebug(visibleUnseenMessageCount > 0);
|
|
||||||
|
|
||||||
NSUInteger missingUnseenSafetyNumberChangeCount = 0;
|
|
||||||
if (hasMoreUnseenMessages) {
|
|
||||||
NSMutableSet<NSData *> *missingUnseenSafetyNumberChanges = [NSMutableSet set];
|
|
||||||
for (TSInvalidIdentityKeyErrorMessage *safetyNumberChange in blockingSafetyNumberChanges) {
|
|
||||||
BOOL isUnseen = safetyNumberChange.sortId >= firstUnseenSortId.unsignedLongLongValue;
|
|
||||||
if (!isUnseen) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL isMissing = safetyNumberChange.sortId < interactionAfterUnreadIndicator.sortId;
|
|
||||||
if (!isMissing) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@try {
|
|
||||||
NSData *_Nullable newIdentityKey = [safetyNumberChange throws_newIdentityKey];
|
|
||||||
if (newIdentityKey == nil) {
|
|
||||||
OWSFailDebug(@"Safety number change was missing it's new identity key.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[missingUnseenSafetyNumberChanges addObject:newIdentityKey];
|
|
||||||
} @catch (NSException *exception) {
|
|
||||||
OWSFailDebug(@"exception: %@", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the de-duplicated "blocking" safety number changes and all
|
|
||||||
// of the "non-blocking" safety number changes.
|
|
||||||
missingUnseenSafetyNumberChangeCount
|
|
||||||
= (missingUnseenSafetyNumberChanges.count + nonBlockingSafetyNumberChanges.count);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSInteger unreadIndicatorPosition = visibleUnseenMessageCount;
|
|
||||||
|
|
||||||
dynamicInteractions.unreadIndicator =
|
|
||||||
[[OWSUnreadIndicator alloc] initWithFirstUnseenSortId:firstUnseenSortId.unsignedLongLongValue
|
|
||||||
hasMoreUnseenMessages:hasMoreUnseenMessages
|
|
||||||
missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount
|
|
||||||
unreadIndicatorPosition:unreadIndicatorPosition];
|
|
||||||
OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.firstUnseenSortId);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (nullable NSNumber *)focusMessagePositionForThread:(TSThread *)thread
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
focusMessageId:(NSString *)focusMessageId
|
|
||||||
{
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(transaction);
|
|
||||||
OWSAssertDebug(focusMessageId);
|
|
||||||
|
|
||||||
YapDatabaseViewTransaction *databaseView = [transaction ext:TSMessageDatabaseViewExtensionName];
|
|
||||||
|
|
||||||
NSString *_Nullable group = nil;
|
|
||||||
NSUInteger index;
|
|
||||||
BOOL success =
|
|
||||||
[databaseView getGroup:&group index:&index forKey:focusMessageId inCollection:TSInteraction.collection];
|
|
||||||
if (!success) {
|
|
||||||
// This might happen if the focus message has disappeared
|
|
||||||
// before this view could appear.
|
|
||||||
OWSFailDebug(@"failed to find focus message index.");
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if (![group isEqualToString:thread.uniqueId]) {
|
|
||||||
OWSFailDebug(@"focus message has invalid group.");
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSUInteger count = [databaseView numberOfItemsInGroup:thread.uniqueId];
|
|
||||||
if (index >= count) {
|
|
||||||
OWSFailDebug(@"focus message has invalid index.");
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSUInteger position = (count - index) - 1;
|
|
||||||
return @(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)shouldShowGroupProfileBannerInThread:(TSThread *)thread blockingManager:(OWSBlockingManager *)blockingManager
|
|
||||||
{
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
OWSAssertDebug(blockingManager);
|
|
||||||
|
|
||||||
if (!thread.isGroupThread) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if ([OWSProfileManager.sharedManager isThreadInProfileWhitelist:thread]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if (![OWSProfileManager.sharedManager hasLocalProfile]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if ([blockingManager isThreadBlocked:thread]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL hasUnwhitelistedMember = NO;
|
|
||||||
NSArray<NSString *> *blockedPhoneNumbers = [blockingManager blockedPhoneNumbers];
|
|
||||||
for (NSString *recipientId in thread.recipientIdentifiers) {
|
|
||||||
if (![blockedPhoneNumbers containsObject:recipientId]
|
|
||||||
&& ![OWSProfileManager.sharedManager isUserInProfileWhitelist:recipientId]) {
|
|
||||||
hasUnwhitelistedMember = YES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasUnwhitelistedMember) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyContactThread:(TSThread *)thread
|
|
||||||
{
|
|
||||||
OWSAssertDebug(thread);
|
|
||||||
|
|
||||||
if (thread.isGroupThread) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if ([OWSProfileManager.sharedManager isThreadInProfileWhitelist:thread]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if (!thread.shouldThreadBeVisible) {
|
|
||||||
[OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread];
|
|
||||||
return YES;
|
|
||||||
} else {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Delete Content
|
|
||||||
|
|
||||||
+ (void)deleteAllContent
|
|
||||||
{
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
|
|
||||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
||||||
[self removeAllObjectsInCollection:[TSThread collection]
|
|
||||||
class:[TSThread class]
|
|
||||||
transaction:transaction];
|
|
||||||
[self removeAllObjectsInCollection:[TSInteraction collection]
|
|
||||||
class:[TSInteraction class]
|
|
||||||
transaction:transaction];
|
|
||||||
[self removeAllObjectsInCollection:[TSAttachment collection]
|
|
||||||
class:[TSAttachment class]
|
|
||||||
transaction:transaction];
|
|
||||||
@try {
|
|
||||||
[self removeAllObjectsInCollection:[SignalRecipient collection]
|
|
||||||
class:[SignalRecipient class]
|
|
||||||
transaction:transaction];
|
|
||||||
} @catch (NSException *exception) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
[TSAttachmentStream deleteAttachments];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)removeAllObjectsInCollection:(NSString *)collection
|
|
||||||
class:(Class) class
|
|
||||||
transaction:(YapDatabaseReadWriteTransaction *)transaction {
|
|
||||||
OWSAssertDebug(collection.length > 0);
|
|
||||||
OWSAssertDebug(class);
|
|
||||||
OWSAssertDebug(transaction);
|
|
||||||
|
|
||||||
NSArray<NSString *> *_Nullable uniqueIds = [transaction allKeysInCollection:collection];
|
|
||||||
if (!uniqueIds) {
|
|
||||||
OWSFailDebug(@"couldn't load uniqueIds for collection: %@.", collection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OWSLogInfo(@"Deleting %lu objects from: %@", (unsigned long)uniqueIds.count, collection);
|
|
||||||
NSUInteger count = 0;
|
|
||||||
for (NSString *uniqueId in uniqueIds) {
|
|
||||||
// We need to fetch each object, since [TSYapDatabaseObject removeWithTransaction:] sometimes does important
|
|
||||||
// work.
|
|
||||||
TSYapDatabaseObject *_Nullable object = [class fetchObjectWithUniqueID:uniqueId transaction:transaction];
|
|
||||||
if (!object) {
|
|
||||||
OWSFailDebug(@"couldn't load object for deletion: %@.", collection);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
[object removeWithTransaction:transaction];
|
|
||||||
count++;
|
|
||||||
};
|
|
||||||
OWSLogInfo(@"Deleted %lu/%lu objects from: %@", (unsigned long)count, (unsigned long)uniqueIds.count, collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Find Content
|
|
||||||
|
|
||||||
+ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp
|
|
||||||
authorId:(NSString *)authorId
|
|
||||||
threadUniqueId:(NSString *)threadUniqueId
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
{
|
|
||||||
OWSAssertDebug(timestamp > 0);
|
|
||||||
OWSAssertDebug(authorId.length > 0);
|
|
||||||
|
|
||||||
NSString *localNumber = [TSAccountManager localNumber];
|
|
||||||
if (localNumber.length < 1) {
|
|
||||||
OWSFailDebug(@"missing long number.");
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray<TSInteraction *> *interactions =
|
|
||||||
[TSInteraction interactionsWithTimestamp:timestamp
|
|
||||||
filter:^(TSInteraction *interaction) {
|
|
||||||
NSString *_Nullable messageAuthorId = nil;
|
|
||||||
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
|
||||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction;
|
|
||||||
messageAuthorId = incomingMessage.authorId;
|
|
||||||
} else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
|
|
||||||
messageAuthorId = localNumber;
|
|
||||||
}
|
|
||||||
if (messageAuthorId.length < 1) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![authorId isEqualToString:messageAuthorId]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if (![interaction.uniqueThreadId isEqualToString:threadUniqueId]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
withTransaction:transaction];
|
|
||||||
if (interactions.count < 1) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if (interactions.count > 1) {
|
|
||||||
// In case of collision, take the first.
|
|
||||||
OWSLogError(@"more than one matching interaction in thread.");
|
|
||||||
}
|
|
||||||
return interactions.firstObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
Loading…
Reference in New Issue