From 6de645a8bbdf91c9c9be39a24a5db28be1424e97 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 12 May 2022 16:40:59 +1000 Subject: [PATCH 01/16] avoid unnecessary UI refresh --- Session/Conversations/ConversationSearch.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Session/Conversations/ConversationSearch.swift b/Session/Conversations/ConversationSearch.swift index 7b7ed47b1..fb97e0f77 100644 --- a/Session/Conversations/ConversationSearch.swift +++ b/Session/Conversations/ConversationSearch.swift @@ -32,6 +32,8 @@ public class ConversationSearchController : NSObject { @objc public let resultsBar: SearchResultsBar = SearchResultsBar() + + private var lastSearchText: String? // MARK: Initializer @@ -88,8 +90,10 @@ extension ConversationSearchController : UISearchResultsUpdating { return } let searchText = FullTextSearchFinder.normalize(text: rawSearchText) + lastSearchText = searchText guard searchText.count >= ConversationSearchController.kMinimumSearchTextLength else { + lastSearchText = nil self.resultsBar.updateResults(resultSet: nil) self.delegate?.conversationSearchController(self, didUpdateSearchResults: nil) return @@ -102,7 +106,7 @@ extension ConversationSearchController : UISearchResultsUpdating { } resultSet = self.dbSearcher.searchWithinConversation(thread: self.thread, searchText: searchText, transaction: transaction) }, completionBlock: { [weak self] in - guard let self = self else { + guard let self = self, searchText == self.lastSearchText else { return } self.resultsBar.updateResults(resultSet: resultSet) @@ -121,7 +125,7 @@ extension ConversationSearchController : SearchResultsBarDelegate { return } - BenchEventStart(title: "Conversation Search Nav", eventId: "Conversation Search Nav: \(searchResult.messageId)") +// BenchEventStart(title: "Conversation Search Nav", eventId: "Conversation Search Nav: \(searchResult.messageId)") self.delegate?.conversationSearchController(self, didSelectMessageId: searchResult.messageId) } } From 531ddcb0793a9597b56fda178b4dfc8a40b90872 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Fri, 13 May 2022 11:54:36 +1000 Subject: [PATCH 02/16] Replaced the GoogleWebRTC library (deprecated) with WebRTC-lib --- Podfile | 2 +- Podfile.lock | 10 ++++----- .../Calls/Views & Modals/CallVideoView.swift | 17 ++++++++------- .../Calls/Views & Modals/MiniCallView.swift | 21 +++++++++---------- .../Calls/WebRTCSession+DataChannel.swift | 2 +- .../Calls/WebRTCSession+MessageHandling.swift | 4 ++-- SessionMessagingKit/Calls/WebRTCSession.swift | 20 +++++++++--------- 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Podfile b/Podfile index 355894d35..373a26f8c 100644 --- a/Podfile +++ b/Podfile @@ -10,7 +10,7 @@ abstract_target 'GlobalDependencies' do pod 'CryptoSwift' pod 'Sodium', '~> 0.9.1' pod 'YapDatabase/SQLCipher', :git => 'https://github.com/oxen-io/session-ios-yap-database.git', branch: 'signal-release' - pod 'GoogleWebRTC' + pod 'WebRTC-lib' pod 'SocketRocket', '~> 0.5.1' target 'Session' do diff --git a/Podfile.lock b/Podfile.lock index 36f51c51c..eae808253 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -21,7 +21,6 @@ PODS: - Curve25519Kit (2.1.0): - CocoaLumberjack - SignalCoreKit - - GoogleWebRTC (1.1.31999) - Mantle (2.1.0): - Mantle/extobjc (= 2.1.0) - Mantle/extobjc (2.1.0) @@ -52,6 +51,7 @@ PODS: - SQLCipher/standard (4.5.0): - SQLCipher/common - SwiftProtobuf (1.5.0) + - WebRTC-lib (96.0.0) - YapDatabase/SQLCipher (3.1.1): - YapDatabase/SQLCipher/Core (= 3.1.1) - YapDatabase/SQLCipher/Extensions (= 3.1.1) @@ -125,7 +125,6 @@ DEPENDENCIES: - AFNetworking - CryptoSwift - Curve25519Kit (from `https://github.com/signalapp/Curve25519Kit.git`) - - GoogleWebRTC - Mantle (from `https://github.com/signalapp/Mantle`, branch `signal-master`) - NVActivityIndicatorView - PromiseKit @@ -136,6 +135,7 @@ DEPENDENCIES: - SocketRocket (~> 0.5.1) - Sodium (~> 0.9.1) - SwiftProtobuf (~> 1.5.0) + - WebRTC-lib - YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`) - YYImage (from `https://github.com/signalapp/YYImage`) - ZXingObjC @@ -145,7 +145,6 @@ SPEC REPOS: - AFNetworking - CocoaLumberjack - CryptoSwift - - GoogleWebRTC - NVActivityIndicatorView - OpenSSL-Universal - PromiseKit @@ -156,6 +155,7 @@ SPEC REPOS: - Sodium - SQLCipher - SwiftProtobuf + - WebRTC-lib - ZXingObjC EXTERNAL SOURCES: @@ -195,7 +195,6 @@ SPEC CHECKSUMS: CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 CryptoSwift: a532e74ed010f8c95f611d00b8bbae42e9fe7c17 Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6 - GoogleWebRTC: b39a78c4f5cc6b0323415b9233db03a2faa7b0f0 Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2 @@ -208,10 +207,11 @@ SPEC CHECKSUMS: Sodium: 23d11554ecd556196d313cf6130d406dfe7ac6da SQLCipher: 98dc22f27c0b1790d39e710d440f22a466ebdb59 SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2 + WebRTC-lib: 508fe02efa0c1a3a8867082a77d24c9be5d29aeb YapDatabase: b418a4baa6906e8028748938f9159807fd039af4 YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 610d1ebc4e559cf746dc3ae0ae7c78b011373d4c +PODFILE CHECKSUM: a3d89a6cc8735285fd51348ca05cea71f2c28872 COCOAPODS: 1.11.2 diff --git a/Session/Calls/Views & Modals/CallVideoView.swift b/Session/Calls/Views & Modals/CallVideoView.swift index 6bf5ce6f7..7bf90dcf1 100644 --- a/Session/Calls/Views & Modals/CallVideoView.swift +++ b/Session/Calls/Views & Modals/CallVideoView.swift @@ -2,12 +2,11 @@ import WebRTC import Foundation -#if arch(arm64) -// Note: 'RTCMTLVideoView' requires arm64 (so won't work on the simulator which -// we need to build for x86_64 due to WebRTC not supporting arm64 simulator builds) -typealias TargetView = RTCMTLVideoView -#else +#if targetEnvironment(simulator) +// Note: 'RTCMTLVideoView' doesn't seem to work on the simulator so use 'RTCEAGLVideoView' instead typealias TargetView = RTCEAGLVideoView +#else +typealias TargetView = RTCMTLVideoView #endif // MARK: RemoteVideoView @@ -17,6 +16,7 @@ class RemoteVideoView: TargetView { override func renderFrame(_ frame: RTCVideoFrame?) { super.renderFrame(frame) guard let frame = frame else { return } + DispatchMainThreadSafe { let frameRatio = Double(frame.height) / Double(frame.width) let frameRotation = frame.rotation @@ -47,7 +47,9 @@ class RemoteVideoView: TargetView { // Assume we're already setup for the correct orientation. break } -#if arch(arm64) + +#if targetEnvironment(simulator) +#else if let rotationOverride = rotationOverride { self.rotationOverride = NSNumber(value: rotationOverride.rawValue) if [ RTCVideoRotation._0, RTCVideoRotation._180 ].contains(rotationOverride) { @@ -86,7 +88,8 @@ class LocalVideoView: TargetView { // sometimes the rotationOverride is not working // if it is only set once on initialization self.rotationOverride = NSNumber(value: RTCVideoRotation._0.rawValue) -#if arch(arm64) +#if targetEnvironment(simulator) +#else self.videoContentMode = .scaleAspectFill #endif } diff --git a/Session/Calls/Views & Modals/MiniCallView.swift b/Session/Calls/Views & Modals/MiniCallView.swift index dec4bbe9c..86d17e508 100644 --- a/Session/Calls/Views & Modals/MiniCallView.swift +++ b/Session/Calls/Views & Modals/MiniCallView.swift @@ -16,9 +16,16 @@ final class MiniCallView: UIView, RTCVideoViewDelegate { private var top: NSLayoutConstraint? private var bottom: NSLayoutConstraint? -#if arch(arm64) - // Note: 'RTCMTLVideoView' requires arm64 (so won't work on the simulator which - // we need to build for x86_64 due to WebRTC not supporting arm64 simulator builds) +#if targetEnvironment(simulator) + // Note: 'RTCMTLVideoView' doesn't seem to work on the simulator so use 'RTCEAGLVideoView' instead + private lazy var remoteVideoView: RTCEAGLVideoView = { + let result = RTCEAGLVideoView() + result.delegate = self + result.alpha = self.callVC.call.isRemoteVideoEnabled ? 1 : 0 + result.backgroundColor = .black + return result + }() +#else private lazy var remoteVideoView: RTCMTLVideoView = { let result = RTCMTLVideoView() result.delegate = self @@ -27,14 +34,6 @@ final class MiniCallView: UIView, RTCVideoViewDelegate { result.backgroundColor = .black return result }() -#else - private lazy var remoteVideoView: RTCEAGLVideoView = { - let result = RTCEAGLVideoView() - result.delegate = self - result.alpha = self.callVC.call.isRemoteVideoEnabled ? 1 : 0 - result.backgroundColor = .black - return result - }() #endif // MARK: Initialization diff --git a/SessionMessagingKit/Calls/WebRTCSession+DataChannel.swift b/SessionMessagingKit/Calls/WebRTCSession+DataChannel.swift index 81805f70a..1180d1618 100644 --- a/SessionMessagingKit/Calls/WebRTCSession+DataChannel.swift +++ b/SessionMessagingKit/Calls/WebRTCSession+DataChannel.swift @@ -8,7 +8,7 @@ extension WebRTCSession: RTCDataChannelDelegate { dataChannelConfiguration.isOrdered = true dataChannelConfiguration.isNegotiated = true dataChannelConfiguration.channelId = 548 - guard let dataChannel = peerConnection.dataChannel(forLabel: "CONTROL", configuration: dataChannelConfiguration) else { + guard let dataChannel = peerConnection?.dataChannel(forLabel: "CONTROL", configuration: dataChannelConfiguration) else { SNLog("[Calls] Couldn't create data channel.") return nil } diff --git a/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift b/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift index f8a760779..37e311cb1 100644 --- a/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift +++ b/SessionMessagingKit/Calls/WebRTCSession+MessageHandling.swift @@ -4,12 +4,12 @@ extension WebRTCSession { public func handleICECandidates(_ candidate: [RTCIceCandidate]) { SNLog("[Calls] Received ICE candidate message.") - candidate.forEach { peerConnection.add($0) } + candidate.forEach { peerConnection?.add($0) } } public func handleRemoteSDP(_ sdp: RTCSessionDescription, from sessionID: String) { SNLog("[Calls] Received remote SDP: \(sdp.sdp).") - peerConnection.setRemoteDescription(sdp, completionHandler: { [weak self] error in + peerConnection?.setRemoteDescription(sdp, completionHandler: { [weak self] error in if let error = error { SNLog("[Calls] Couldn't set SDP due to error: \(error).") } else { diff --git a/SessionMessagingKit/Calls/WebRTCSession.swift b/SessionMessagingKit/Calls/WebRTCSession.swift index 9f6f26acb..8d217549d 100644 --- a/SessionMessagingKit/Calls/WebRTCSession.swift +++ b/SessionMessagingKit/Calls/WebRTCSession.swift @@ -35,7 +35,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { /// Represents a WebRTC connection between the user and a remote peer. Provides methods to connect to a /// remote peer, maintain and monitor the connection, and close the connection once it's no longer needed. - internal lazy var peerConnection: RTCPeerConnection = { + internal lazy var peerConnection: RTCPeerConnection? = { let configuration = RTCConfiguration() if let defaultICEServer = defaultICEServer { configuration.iceServers = [ RTCIceServer(urlStrings: defaultICEServer.urls, username: defaultICEServer.username, credential: defaultICEServer.password) ] @@ -67,7 +67,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { }() internal lazy var remoteVideoTrack: RTCVideoTrack? = { - return peerConnection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack + return peerConnection?.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack }() // Data Channel @@ -94,8 +94,8 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { self.uuid = uuid super.init() let mediaStreamTrackIDS = ["ARDAMS"] - peerConnection.add(audioTrack, streamIds: mediaStreamTrackIDS) - peerConnection.add(localVideoTrack, streamIds: mediaStreamTrackIDS) + peerConnection?.add(audioTrack, streamIds: mediaStreamTrackIDS) + peerConnection?.add(localVideoTrack, streamIds: mediaStreamTrackIDS) // Configure audio session configureAudioSession() @@ -125,12 +125,12 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { SNLog("[Calls] Sending offer message.") guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) } let (promise, seal) = Promise.pending() - peerConnection.offer(for: mediaConstraints(isRestartingICEConnection)) { [weak self] sdp, error in + peerConnection?.offer(for: mediaConstraints(isRestartingICEConnection)) { [weak self] sdp, error in if let error = error { seal.reject(error) } else { guard let self = self, let sdp = self.correctSessionDescription(sdp: sdp) else { preconditionFailure() } - self.peerConnection.setLocalDescription(sdp) { error in + self.peerConnection?.setLocalDescription(sdp) { error in if let error = error { print("Couldn't initiate call due to error: \(error).") return seal.reject(error) @@ -157,12 +157,12 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { SNLog("[Calls] Sending answer message.") guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) } let (promise, seal) = Promise.pending() - peerConnection.answer(for: mediaConstraints(false)) { [weak self] sdp, error in + peerConnection?.answer(for: mediaConstraints(false)) { [weak self] sdp, error in if let error = error { seal.reject(error) } else { guard let self = self, let sdp = self.correctSessionDescription(sdp: sdp) else { preconditionFailure() } - self.peerConnection.setLocalDescription(sdp) { error in + self.peerConnection?.setLocalDescription(sdp) { error in if let error = error { print("Couldn't accept call due to error: \(error).") return seal.reject(error) @@ -221,7 +221,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { } public func dropConnection() { - peerConnection.close() + peerConnection?.close() } private func mediaConstraints(_ isRestartingICEConnection: Bool) -> RTCMediaConstraints { @@ -263,7 +263,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { if state == .connected { delegate?.webRTCIsConnected() } else if state == .disconnected { - if self.peerConnection.signalingState == .stable { + if self.peerConnection?.signalingState == .stable { delegate?.reconnectIfNeeded() } } From ecb914096918d52df390d71bff1473b236bec544 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 13 May 2022 15:28:17 +1000 Subject: [PATCH 03/16] send closed group message to default namespace as well during hardfork 19.0 --- SessionSnodeKit/SnodeAPI.swift | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index fd669ea17..015fba9d3 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -499,8 +499,7 @@ public final class SnodeAPI : NSObject { // MARK: Store public static func sendMessage(_ message: SnodeMessage, isClosedGroupMessage: Bool, isConfigMessage: Bool) -> Promise> { - let namespace = isClosedGroupMessage ? closedGroupNamespace : defaultNamespace - return sendMessageUnauthenticated(message, namespace: namespace) + return sendMessageUnauthenticated(message, isClosedGroupMessage: isClosedGroupMessage) } // Not in use until we can batch delete and store config messages @@ -534,18 +533,33 @@ public final class SnodeAPI : NSObject { return promise } - private static func sendMessageUnauthenticated(_ message: SnodeMessage, namespace: Int) -> Promise> { + private static func sendMessageUnauthenticated(_ message: SnodeMessage, isClosedGroupMessage: Bool) -> Promise> { let (promise, seal) = Promise>.pending() let publicKey = Features.useTestnet ? message.recipient.removing05PrefixIfNeeded() : message.recipient Threading.workQueue.async { getTargetSnodes(for: publicKey).map2 { targetSnodes in + var rawResponsePromises: Set = Set() var parameters = message.toJSON() - parameters["namespace"] = namespace - return Set(targetSnodes.map { targetSnode in - attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { + parameters["namespace"] = isClosedGroupMessage ? closedGroupNamespace : defaultNamespace + for targetSnode in targetSnodes { + let rawResponsePromise = attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { invoke(.sendMessage, on: targetSnode, associatedWith: publicKey, parameters: parameters) } - }) + rawResponsePromises.insert(rawResponsePromise) + } + + // Send closed group messages to default namespace as well + if hardfork == 19 && softfork == 0 && isClosedGroupMessage { + parameters["namespace"] = defaultNamespace + for targetSnode in targetSnodes { + let rawResponsePromise = attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { + invoke(.sendMessage, on: targetSnode, associatedWith: publicKey, parameters: parameters) + } + rawResponsePromises.insert(rawResponsePromise) + } + } + + return rawResponsePromises }.done2 { seal.fulfill($0) }.catch2 { seal.reject($0) } } return promise From f92ae4084ce5c2215910fa85f335f95f34a9dc12 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 13 May 2022 16:28:01 +1000 Subject: [PATCH 04/16] bump up build number --- Session.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index f1a416dd1..32cc166db 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -5181,7 +5181,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5254,7 +5254,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -5320,7 +5320,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5394,7 +5394,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -6330,7 +6330,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6403,7 +6403,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 343; + CURRENT_PROJECT_VERSION = 344; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From 2edcba3342c47f2cd21c5b6b4d43e919d65e6798 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 24 May 2022 09:36:28 +1000 Subject: [PATCH 05/16] fix https://github.com/oxen-io/session-ios/issues/628 --- Session/Conversations/ConversationVC.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index 057f5e0b0..dea1978bf 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -429,12 +429,12 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat Storage.write { transaction in self.thread.setDraft(text, transaction: transaction) } - self.resignFirstResponder() } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) mediaCache.removeAllObjects() + self.resignFirstResponder() } override func appDidBecomeActive(_ notification: Notification) { From 567a9befd4480b2b2ba578e71d7ce20fdf5eda3a Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 25 May 2022 10:45:21 +1000 Subject: [PATCH 06/16] prevent call info message fires multiple notification --- Session.xcodeproj/project.pbxproj | 4 +++ Session/Meta/AppDelegate.swift | 33 +++++++++++-------- .../Database/Storage+Calls.swift | 16 +++++++++ SessionMessagingKit/Storage.swift | 5 +++ 4 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 SessionMessagingKit/Database/Storage+Calls.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 32cc166db..e63d23d98 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -140,6 +140,7 @@ 7B251C3627D82D9E001A6284 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */; }; 7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */; }; + 7B703747283CA919000DCF35 /* Storage+Calls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B703746283CA919000DCF35 /* Storage+Calls.swift */; }; 7B7CB189270430D20079FF93 /* CallMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB188270430D20079FF93 /* CallMessageView.swift */; }; 7B7CB18B270591630079FF93 /* ShareLogsModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB18A270591630079FF93 /* ShareLogsModal.swift */; }; 7B7CB18E270D066F0079FF93 /* IncomingCallBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB18D270D066F0079FF93 /* IncomingCallBanner.swift */; }; @@ -1129,6 +1130,7 @@ 7B2DB2AD26F1B0FF0035B509 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Localizable.strings; sourceTree = ""; }; 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsendRequest.swift; sourceTree = ""; }; 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessageView.swift; sourceTree = ""; }; + 7B703746283CA919000DCF35 /* Storage+Calls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Calls.swift"; sourceTree = ""; }; 7B7CB188270430D20079FF93 /* CallMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMessageView.swift; sourceTree = ""; }; 7B7CB18A270591630079FF93 /* ShareLogsModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareLogsModal.swift; sourceTree = ""; }; 7B7CB18D270D066F0079FF93 /* IncomingCallBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallBanner.swift; sourceTree = ""; }; @@ -2754,6 +2756,7 @@ B8D8F19225661BF80092EF10 /* Storage+Messaging.swift */, B8D8F18825661BA50092EF10 /* Storage+OpenGroups.swift */, C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */, + 7B703746283CA919000DCF35 /* Storage+Calls.swift */, C33FDA69255A57F900E217F9 /* SSKPreferences.swift */, C33FDB25255A580900E217F9 /* TSDatabaseSecondaryIndexes.h */, C33FDB20255A580900E217F9 /* TSDatabaseSecondaryIndexes.m */, @@ -4701,6 +4704,7 @@ C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */, C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */, C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */, + 7B703747283CA919000DCF35 /* Storage+Calls.swift in Sources */, C352A32F2557549C00338F3E /* NotifyPNServerJob.swift in Sources */, 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */, C300A5F22554B09800555489 /* MessageSender.swift in Sources */, diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 0685f19d1..540d734bd 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -56,10 +56,15 @@ extension AppDelegate { } } - private func insertCallInfoMessage(for message: CallMessage, using transaction: YapDatabaseReadWriteTransaction) -> TSInfoMessage { + private func insertCallInfoMessage(for message: CallMessage, using transaction: YapDatabaseReadWriteTransaction) -> TSInfoMessage? { + guard let sender = message.sender, let uuid = message.uuid else { return nil } + var receivedCalls = Storage.shared.getReceivedCalls(for: sender, using: transaction) + guard !receivedCalls.contains(uuid) else { return nil } let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) let infoMessage = TSInfoMessage.from(message, associatedWith: thread) infoMessage.save(with: transaction) + receivedCalls.insert(message.uuid!) + Storage.shared.setReceivedCalls(to: receivedCalls, for: sender, using: transaction) return infoMessage } @@ -78,20 +83,22 @@ extension AppDelegate { guard CurrentAppContext().isMainApp else { return } guard let timestamp = message.sentTimestamp, TimestampUtils.isWithinOneMinute(timestamp: timestamp) else { // Add missed call message for call offer messages from more than one minute - let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) - infoMessage.updateCallInfoMessage(.missed, using: transaction) - let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) - SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + if let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) { + infoMessage.updateCallInfoMessage(.missed, using: transaction) + let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + } return } guard SSKPreferences.areCallsEnabled else { - let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) - infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction) - let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) - SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) - let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender! - DispatchQueue.main.async { - self.showMissedCallTipsIfNeeded(caller: contactName) + if let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) { + infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction) + let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender! + DispatchQueue.main.async { + self.showMissedCallTipsIfNeeded(caller: contactName) + } } return } @@ -106,7 +113,7 @@ extension AppDelegate { // Handle UI if let caller = message.sender, let uuid = message.uuid { let call = SessionCall(for: caller, uuid: uuid, mode: .answer) - call.callMessageID = infoMessage.uniqueId + call.callMessageID = infoMessage?.uniqueId self.showCallUIForCall(call) } } diff --git a/SessionMessagingKit/Database/Storage+Calls.swift b/SessionMessagingKit/Database/Storage+Calls.swift new file mode 100644 index 000000000..c1de95a9c --- /dev/null +++ b/SessionMessagingKit/Database/Storage+Calls.swift @@ -0,0 +1,16 @@ + +extension Storage { + + private static let receivedCallsCollection = "LokiReceivedCallsCollection" + + public func getReceivedCalls(for publicKey: String, using transaction: Any) -> Set { + var result: Set? + guard let transaction = transaction as? YapDatabaseReadTransaction else { return [] } + result = transaction.object(forKey: publicKey, inCollection: Storage.receivedCallsCollection) as? Set + return result ?? [] + } + + public func setReceivedCalls(to receivedCalls: Set, for publicKey: String, using transaction: Any) { + (transaction as! YapDatabaseReadWriteTransaction).setObject(receivedCalls, forKey: publicKey, inCollection: Storage.receivedCallsCollection) + } +} diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index 43508d40b..aba77646a 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -96,6 +96,11 @@ public protocol SessionMessagingKitStorageProtocol { func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsIncomingMessageID: String, using transaction: Any) /// Also touches the associated message. func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any) + + // MARK: - Calls + + func getReceivedCalls(for publicKey: String, using transaction: Any) -> Set + func setReceivedCalls(to receivedCalls: Set, for publicKey: String, using transaction: Any) } extension Storage: SessionMessagingKitStorageProtocol, SessionSnodeKitStorageProtocol {} From b54de30d5c203895639c1db134633a9fd8aec340 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 25 May 2022 11:04:54 +1000 Subject: [PATCH 07/16] potentially fix path rebuilding issue --- SessionSnodeKit/SnodeAPI.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index 015fba9d3..6c4f9e1ab 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -752,6 +752,9 @@ public final class SnodeAPI : NSObject { case 500, 502, 503: // The snode is unreachable handleBadSnode() + case 404: + // May caused by invalid open groups + SNLog("Can't reach the server.") case 406: SNLog("The user's clock is out of sync with the service node network.") return Error.clockOutOfSync From 113a931645562313ba8c4dbf1a343a6a8fa02ffb Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 25 May 2022 14:57:05 +1000 Subject: [PATCH 08/16] add access for selected photos permission --- .../ImagePickerController.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Session/Media Viewing & Editing/ImagePickerController.swift b/Session/Media Viewing & Editing/ImagePickerController.swift index 3500fdc99..233f78d53 100644 --- a/Session/Media Viewing & Editing/ImagePickerController.swift +++ b/Session/Media Viewing & Editing/ImagePickerController.swift @@ -92,6 +92,19 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat selectionPanGesture.delegate = self self.selectionPanGesture = selectionPanGesture collectionView.addGestureRecognizer(selectionPanGesture) + + if #available(iOS 14, *) { + if PHPhotoLibrary.authorizationStatus(for: .readWrite) == .limited { + let addSeletedPhotoButton = UIBarButtonItem.init(barButtonSystemItem: .add, target: self, action: #selector(addSelectedPhoto)) + self.navigationItem.rightBarButtonItem = addSeletedPhotoButton + } + } + } + + @objc func addSelectedPhoto(_ sender: Any) { + if #available(iOS 14, *) { + PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self) + } } var selectionPanGesture: UIPanGestureRecognizer? From 92123df86532428c6251be2ce19af75d76acf59f Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 25 May 2022 16:31:33 +1000 Subject: [PATCH 09/16] fix typing indicator --- Session/Conversations/ConversationViewModel.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index 181d31b27..b034f85e0 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -333,6 +333,7 @@ NS_ASSUME_NONNULL_BEGIN selector:@selector(applicationWillEnterForeground:) name:OWSApplicationWillEnterForegroundNotification object:nil]; + [self addNotificationListeners]; } - (void)viewDidLoad From 35ef87cfc2acb27f9b4ddc6877d3e3232e422cd5 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 26 May 2022 09:41:16 +1000 Subject: [PATCH 10/16] clean --- Session/Conversations/ConversationViewModel.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index b034f85e0..0c816e8a1 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -336,13 +336,6 @@ NS_ASSUME_NONNULL_BEGIN [self addNotificationListeners]; } -- (void)viewDidLoad -{ - [self addNotificationListeners]; - - [self touchDbAsync]; -} - - (void)touchDbAsync { // See comments in primaryStorage.touchDbAsync. From ab7bd24ad56cfe08815e9d984e13c78845a00e99 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 26 May 2022 10:12:23 +1000 Subject: [PATCH 11/16] change in-conversation search result highlight UI to match with global search --- Session/Conversations/ConversationVC.swift | 5 +++- .../Content Views/LinkPreviewView.swift | 2 +- .../Message Cells/VisibleMessageCell.swift | 25 ++++--------------- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index dea1978bf..201ff2ec8 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -922,7 +922,10 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat } func conversationSearchController(_ conversationSearchController: ConversationSearchController, didSelectMessageId interactionID: String) { - scrollToInteraction(with: interactionID) + scrollToInteraction(with: interactionID, highlighted: true) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { + self.highlightFocusedMessageIfNeeded() + } } func scrollToInteraction(with interactionID: String, position: UITableView.ScrollPosition = .middle, isAnimated: Bool = true, highlighted: Bool = false) { diff --git a/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift b/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift index 0de2178b6..086adece9 100644 --- a/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift +++ b/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift @@ -146,7 +146,7 @@ final class LinkPreviewView : UIView { // Body text view bodyTextViewContainer.subviews.forEach { $0.removeFromSuperview() } if let viewItem = viewItem { - let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, searchText: delegate.lastSearchedText, delegate: delegate) + let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, delegate: delegate) self.bodyTextView = bodyTextView bodyTextViewContainer.addSubview(bodyTextView) bodyTextView.pin(to: bodyTextViewContainer, withInset: 12) diff --git a/Session/Conversations/Message Cells/VisibleMessageCell.swift b/Session/Conversations/Message Cells/VisibleMessageCell.swift index f247fcb47..640b427da 100644 --- a/Session/Conversations/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations/Message Cells/VisibleMessageCell.swift @@ -351,7 +351,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { stackView.addArrangedSubview(quoteViewContainer) } // Body text view - let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate?.lastSearchedText, delegate: self) + let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self) self.bodyTextView = bodyTextView stackView.addArrangedSubview(bodyTextView) // Constraints @@ -383,7 +383,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 { let inset: CGFloat = 12 let maxWidth = size.width - 2 * inset - let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate?.lastSearchedText, delegate: self) + let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self) self.bodyTextView = bodyTextView stackView.addArrangedSubview(UIView(wrapping: bodyTextView, withInsets: UIEdgeInsets(top: 0, left: inset, bottom: inset, right: inset))) } @@ -420,9 +420,8 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { let documentView = DocumentView(viewItem: viewItem, textColor: bodyLabelTextColor) stackView.addArrangedSubview(documentView) // Body text view - if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0, - let delegate = delegate { // delegate should always be set at this point - let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate.lastSearchedText, delegate: self) + if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 { + let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self) self.bodyTextView = bodyTextView stackView.addArrangedSubview(bodyTextView) } @@ -704,7 +703,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { return isGroupThread && viewItem.shouldShowSenderProfilePicture && senderSessionID != nil } - static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, searchText: String?, delegate: UITextViewDelegate & BodyTextViewDelegate) -> UITextView { + static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, delegate: UITextViewDelegate & BodyTextViewDelegate) -> UITextView { // Take care of: // • Highlighting mentions // • Linkification @@ -718,20 +717,6 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { .font : UIFont.systemFont(ofSize: getFontSize(for: viewItem)) ] let attributedText = NSMutableAttributedString(attributedString: MentionUtilities.highlightMentions(in: message.body ?? "", isOutgoingMessage: isOutgoing, threadID: viewItem.interaction.uniqueThreadId, attributes: attributes)) - if let searchText = searchText, searchText.count >= ConversationSearchController.kMinimumSearchTextLength { - let normalizedSearchText = FullTextSearchFinder.normalize(text: searchText) - do { - let regex = try NSRegularExpression(pattern: NSRegularExpression.escapedPattern(for: normalizedSearchText), options: .caseInsensitive) - let matches = regex.matches(in: attributedText.string, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: (attributedText.string as NSString).length)) - for match in matches { - guard match.range.location + match.range.length < attributedText.length else { continue } - attributedText.addAttribute(.backgroundColor, value: UIColor.white, range: match.range) - attributedText.addAttribute(.foregroundColor, value: UIColor.black, range: match.range) - } - } catch { - // Do nothing - } - } result.attributedText = attributedText result.dataDetectorTypes = .link result.backgroundColor = .clear From 987db2f7ab5322d2793349bb8ef14e73311c466e Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 26 May 2022 13:43:44 +1000 Subject: [PATCH 12/16] fix an edge case which may cause old messages coming back --- Session/Utilities/BackgroundPoller.swift | 10 ++++++++-- .../Pollers/ClosedGroupPoller.swift | 5 ++++- .../Sending & Receiving/Pollers/Poller.swift | 5 ++++- SessionSnodeKit/SnodeAPI.swift | 20 +++++++++++++------ SessionSnodeKit/Storage+SnodeAPI.swift | 1 - 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Session/Utilities/BackgroundPoller.swift b/Session/Utilities/BackgroundPoller.swift index f2f0d5f41..43b0199f4 100644 --- a/Session/Utilities/BackgroundPoller.swift +++ b/Session/Utilities/BackgroundPoller.swift @@ -42,16 +42,19 @@ public final class BackgroundPoller : NSObject { return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) { SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey).then(on: DispatchQueue.main) { rawResponse -> Promise in let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey) + var processedMessages: [JSON] = [] let promises = messages.compactMap { json -> Promise? in // Use a best attempt approach here; we don't want to fail the entire process if one of the // messages failed to parse. guard let envelope = SNProtoEnvelope.from(json), let data = try? envelope.serializedData() else { return nil } let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true) + processedMessages.append(json) return job.execute() } - // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value + // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes` SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: SnodeAPI.defaultNamespace, associatedWith: publicKey, from: lastRawMessage) + SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: publicKey) return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects } @@ -82,16 +85,19 @@ public final class BackgroundPoller : NSObject { for result in results { if case .fulfilled(let rawResponse) = result { let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey) + var processedMessages: [JSON] = [] let jobPromises = messages.compactMap { json -> Promise? in // Use a best attempt approach here; we don't want to fail the entire process if one of the // messages failed to parse. guard let envelope = SNProtoEnvelope.from(json), let data = try? envelope.serializedData() else { return nil } let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true) + processedMessages.append(json) return job.execute() } - // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value + // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes` SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: namespaces[index], associatedWith: publicKey, from: lastRawMessage) + SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: publicKey) promises += jobPromises } index += 1 diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift b/SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift index 77085c778..70a3698fd 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift @@ -122,6 +122,7 @@ public final class ClosedGroupPoller : NSObject { if !rawMessages.isEmpty { SNLog("Received \(rawMessages.count) new message(s) in closed group with public key: \(groupPublicKey).") } + var processedMessages: [JSON] = [] rawMessages.forEach { json in guard let envelope = SNProtoEnvelope.from(json) else { return } do { @@ -130,13 +131,15 @@ public final class ClosedGroupPoller : NSObject { SNMessagingKitConfiguration.shared.storage.write { transaction in SessionMessagingKit.JobQueue.shared.add(job, using: transaction) } + processedMessages.append(json) } catch { SNLog("Failed to deserialize envelope due to error: \(error).") } } - // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value + // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes` SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: SnodeAPI.closedGroupNamespace, associatedWith: groupPublicKey, from: lastRawMessage) + SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: groupPublicKey) } promise.catch2 { error in SNLog("Polling failed for closed group with public key: \(groupPublicKey) due to error: \(error).") diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift b/SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift index 7c50725fb..5a97e1129 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift @@ -98,6 +98,7 @@ public final class Poller : NSObject { if !messages.isEmpty { SNLog("Received \(messages.count) new message(s).") } + var processedMessages: [JSON] = [] messages.forEach { json in guard let envelope = SNProtoEnvelope.from(json) else { return } do { @@ -106,13 +107,15 @@ public final class Poller : NSObject { SNMessagingKitConfiguration.shared.storage.write { transaction in SessionMessagingKit.JobQueue.shared.add(job, using: transaction) } + processedMessages.append(json) } catch { SNLog("Failed to deserialize envelope due to error: \(error).") } } - // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value + // Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes` SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: SnodeAPI.defaultNamespace, associatedWith: userPublicKey, from: lastRawMessage) + SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: userPublicKey) strongSelf.pollCount += 1 if strongSelf.pollCount == Poller.maxPollCount { diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index 6c4f9e1ab..eabce3993 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -705,6 +705,20 @@ public final class SnodeAPI : NSObject { } } + public static func updateReceivedMessages(from messages: [JSON], associatedWith publicKey: String) { + let oldReceivedMessages = SNSnodeKitConfiguration.shared.storage.getReceivedMessages(for: publicKey) + var newReceivedMessages = oldReceivedMessages + for message in messages { + guard let hash = message["hash"] as? String else { continue } + newReceivedMessages.insert(hash) + } + if oldReceivedMessages != newReceivedMessages { + SNSnodeKitConfiguration.shared.storage.writeSync { transaction in + SNSnodeKitConfiguration.shared.storage.setReceivedMessages(to: newReceivedMessages, for: publicKey, using: transaction) + } + } + } + private static func removeDuplicates(from rawMessages: [JSON], associatedWith publicKey: String) -> [JSON] { let oldReceivedMessages = SNSnodeKitConfiguration.shared.storage.getReceivedMessages(for: publicKey) var newReceivedMessages = oldReceivedMessages @@ -717,12 +731,6 @@ public final class SnodeAPI : NSObject { newReceivedMessages.insert(hash) return !isDuplicate } - // Avoid the sync write transaction if possible - if oldReceivedMessages != newReceivedMessages { - SNSnodeKitConfiguration.shared.storage.writeSync { transaction in - SNSnodeKitConfiguration.shared.storage.setReceivedMessages(to: newReceivedMessages, for: publicKey, using: transaction) - } - } return result } diff --git a/SessionSnodeKit/Storage+SnodeAPI.swift b/SessionSnodeKit/Storage+SnodeAPI.swift index f45b6ea63..cbccbc0a6 100644 --- a/SessionSnodeKit/Storage+SnodeAPI.swift +++ b/SessionSnodeKit/Storage+SnodeAPI.swift @@ -110,7 +110,6 @@ extension Storage { if now >= expirationDate { Storage.writeSync { transaction in self.removeLastMessageHashInfo(for: snode, namespace: namespace, associatedWith: publicKey, using: transaction) - self.setReceivedMessages(to: Set(), for: publicKey, using: transaction) } } } From fb610925704190ea97861cc1520b5898cb3c2f13 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 30 May 2022 09:43:07 +1000 Subject: [PATCH 13/16] clean --- Session/Conversations/ConversationSearch.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Session/Conversations/ConversationSearch.swift b/Session/Conversations/ConversationSearch.swift index fb97e0f77..ba721dd4c 100644 --- a/Session/Conversations/ConversationSearch.swift +++ b/Session/Conversations/ConversationSearch.swift @@ -125,7 +125,6 @@ extension ConversationSearchController : SearchResultsBarDelegate { return } -// BenchEventStart(title: "Conversation Search Nav", eventId: "Conversation Search Nav: \(searchResult.messageId)") self.delegate?.conversationSearchController(self, didSelectMessageId: searchResult.messageId) } } From db71664feb7a6a04151db2355d2cc0b18a31d6e1 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 30 May 2022 10:19:32 +1000 Subject: [PATCH 14/16] bump up version & build number --- Session.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index e63d23d98..896b69791 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -5185,7 +5185,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5210,7 +5210,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5258,7 +5258,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -5288,7 +5288,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5324,7 +5324,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5347,7 +5347,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; @@ -5398,7 +5398,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -5426,7 +5426,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; @@ -6334,7 +6334,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6374,7 +6374,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; @@ -6407,7 +6407,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 344; + CURRENT_PROJECT_VERSION = 345; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6447,7 +6447,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 1.12.8; + MARKETING_VERSION = 1.12.9; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; PRODUCT_NAME = Session; From 0237df8023ce260fbc8bbc5b8959929ef42c02ba Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 30 May 2022 13:14:13 +1000 Subject: [PATCH 15/16] fix notification service extension creates duplicated call info message --- Session/Meta/AppDelegate.swift | 2 +- .../NotificationServiceExtension.swift | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 540d734bd..78d0924f6 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -63,7 +63,7 @@ extension AppDelegate { let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) let infoMessage = TSInfoMessage.from(message, associatedWith: thread) infoMessage.save(with: transaction) - receivedCalls.insert(message.uuid!) + receivedCalls.insert(uuid) Storage.shared.setReceivedCalls(to: receivedCalls, for: sender, using: transaction) return infoMessage } diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index db7e22d51..d1d598191 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -78,9 +78,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension guard case .preOffer = callMessage.kind else { return self.completeSilenty() } if !SSKPreferences.areCallsEnabled { if let sender = callMessage.sender, let thread = TSContactThread.fetch(for: sender, using: transaction), !thread.isMessageRequest(using: transaction) { - let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread) - infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction) - SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + self.insertCallInfoMessage(for: callMessage, in: thread, reason: .permissionDenied, using: transaction) } break } @@ -92,9 +90,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension message.kind = .endCall SNLog("[Calls] Sending end call message because there is an ongoing call.") MessageSender.sendNonDurably(message, in: thread, using: transaction).retainUntilComplete() - let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread) - infoMessage.updateCallInfoMessage(.missed, using: transaction) - SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + self.insertCallInfoMessage(for: callMessage, in: thread, reason: .missed, using: transaction) } break } @@ -112,6 +108,18 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension } } } + + private func insertCallInfoMessage(for message: CallMessage, in thread: TSThread, reason: TSInfoMessageCallState, using transaction: YapDatabaseReadWriteTransaction) { + guard let sender = message.sender, let uuid = message.uuid else { return } + var receivedCalls = Storage.shared.getReceivedCalls(for: sender, using: transaction) + if !receivedCalls.contains(uuid) { + let infoMessage = TSInfoMessage.from(message, associatedWith: thread) + infoMessage.updateCallInfoMessage(reason, using: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) + receivedCalls.insert(uuid) + Storage.shared.setReceivedCalls(to: receivedCalls, for: sender, using: transaction) + } + } // MARK: Setup @@ -263,6 +271,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension } // MARK: Poll for open groups + private func pollForOpenGroups() -> [Promise] { var promises: [Promise] = [] let servers = Set(Storage.shared.getAllV2OpenGroups().values.map { $0.server }) From 21fbf57aaa5a30a859100ff8e7432e94125c95c1 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 30 May 2022 13:14:43 +1000 Subject: [PATCH 16/16] update build number --- Session.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 896b69791..aa559ebba 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -5185,7 +5185,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5258,7 +5258,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -5324,7 +5324,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -5398,7 +5398,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -6334,7 +6334,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6407,7 +6407,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 345; + CURRENT_PROJECT_VERSION = 346; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)",