From 6de645a8bbdf91c9c9be39a24a5db28be1424e97 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 12 May 2022 16:40:59 +1000 Subject: [PATCH 01/10] 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 2edcba3342c47f2cd21c5b6b4d43e919d65e6798 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 24 May 2022 09:36:28 +1000 Subject: [PATCH 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 09/10] 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 10/10] 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) } }