diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 7c9176f25..95da2432b 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -607,7 +607,6 @@ FD17D7B327F51E5B00122BE0 /* SSKSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7B227F51E5B00122BE0 /* SSKSetting.swift */; }; FD17D7B827F51ECA00122BE0 /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7B727F51ECA00122BE0 /* Migration.swift */; }; FD17D7BA27F51F2100122BE0 /* TargetMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7B927F51F2100122BE0 /* TargetMigrations.swift */; }; - FD17D7BD27F51F6900122BE0 /* GRDB+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7BC27F51F6900122BE0 /* GRDB+Notifications.swift */; }; FD17D7BF27F51F8200122BE0 /* ColumnExpressible.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7BE27F51F8200122BE0 /* ColumnExpressible.swift */; }; FD17D7C127F5200100122BE0 /* TypedTableDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7C027F5200100122BE0 /* TypedTableDefinition.swift */; }; FD17D7C327F5204C00122BE0 /* Database+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7C227F5204C00122BE0 /* Database+Utilities.swift */; }; @@ -785,6 +784,7 @@ FDD2506E283711D600198BDA /* DifferenceKit+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */; }; FDD250702837199200198BDA /* GarbageCollectionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */; }; FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */; }; + FDE72118286C156E0093DF33 /* ChatSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */; }; FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; }; FDED2E3C282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */; }; FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; }; @@ -1671,7 +1671,6 @@ FD17D7B227F51E5B00122BE0 /* SSKSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKSetting.swift; sourceTree = "<group>"; }; FD17D7B727F51ECA00122BE0 /* Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Migration.swift; sourceTree = "<group>"; }; FD17D7B927F51F2100122BE0 /* TargetMigrations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetMigrations.swift; sourceTree = "<group>"; }; - FD17D7BC27F51F6900122BE0 /* GRDB+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GRDB+Notifications.swift"; sourceTree = "<group>"; }; FD17D7BE27F51F8200122BE0 /* ColumnExpressible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnExpressible.swift; sourceTree = "<group>"; }; FD17D7C027F5200100122BE0 /* TypedTableDefinition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedTableDefinition.swift; sourceTree = "<group>"; }; FD17D7C227F5204C00122BE0 /* Database+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Database+Utilities.swift"; sourceTree = "<group>"; }; @@ -1819,6 +1818,7 @@ FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DifferenceKit+Utilities.swift"; sourceTree = "<group>"; }; FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GarbageCollectionJob.swift; sourceTree = "<group>"; }; FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryNavigationController.swift; sourceTree = "<group>"; }; + FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatSettingsViewController.swift; sourceTree = "<group>"; }; FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRunnerError.swift; sourceTree = "<group>"; }; FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlMessageProcessRecord.swift; sourceTree = "<group>"; }; FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+ReusableView.swift"; sourceTree = "<group>"; }; @@ -2798,6 +2798,7 @@ B886B4A62398B23E00211ABE /* QRCodeVC.swift */, B86BD08523399CEF000F5AE3 /* SeedModal.swift */, B8CCF6422397711F0091D419 /* SettingsVC.swift */, + FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */, 7B7CB18A270591630079FF93 /* ShareLogsModal.swift */, ); path = Settings; @@ -3544,7 +3545,6 @@ FD17D7C427F5206300122BE0 /* ColumnDefinition+Utilities.swift */, FD17D7C227F5204C00122BE0 /* Database+Utilities.swift */, FD17D7C627F5207C00122BE0 /* DatabaseMigrator+Utilities.swift */, - FD17D7BC27F51F6900122BE0 /* GRDB+Notifications.swift */, FDF22210281B5E0B000A4995 /* TableRecord+Utilities.swift */, FDF2220E281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift */, ); @@ -5011,7 +5011,6 @@ FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */, C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */, C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */, - FD17D7BD27F51F6900122BE0 /* GRDB+Notifications.swift in Sources */, FD17D7CA27F546D900122BE0 /* _001_InitialSetupMigration.swift in Sources */, C3D9E4D12567777D0040E4F3 /* OWSMediaUtils.swift in Sources */, C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */, @@ -5276,6 +5275,7 @@ EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */, 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */, B83524A525C3BA4B0089A44F /* InfoMessageCell.swift in Sources */, + FDE72118286C156E0093DF33 /* ChatSettingsViewController.swift in Sources */, B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */, 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */, FDCDB8E02811007F00352A0C /* HomeViewModel.swift in Sources */, diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index bb76b60dc..2aea6b825 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -942,7 +942,8 @@ extension ConversationVC: db, blindedId: sessionId, openGroupServer: openGroupServer, - openGroupPublicKey: openGroupPublicKey + openGroupPublicKey: openGroupPublicKey, + isCheckingForOutbox: false ) return try SessionThread diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index c3c767ae2..73038c611 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -571,6 +571,7 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers } if initialLoad || viewModel.threadData.threadIsMessageRequest != updatedThreadData.threadIsMessageRequest { + messageRequestView.isHidden = (updatedThreadData.threadIsMessageRequest == false) scrollButtonMessageRequestsBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == true) scrollButtonBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == false) } @@ -595,8 +596,13 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers self.viewModel.updateThreadData(updatedThreadData) /// **Note:** This needs to happen **after** we have update the viewModel's thread data - if viewModel.threadData.currentUserIsClosedGroupMember != updatedThreadData.currentUserIsClosedGroupMember { - reloadInputViews() + if initialLoad || viewModel.threadData.currentUserIsClosedGroupMember != updatedThreadData.currentUserIsClosedGroupMember { + if !self.isFirstResponder { + self.becomeFirstResponder() + } + else { + self.reloadInputViews() + } } } diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index 53e2b98cf..2c50fa346 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -627,6 +627,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve ) ) ) + try MessageSender.syncConfiguration(db, forceSyncNow: true) .retainUntilComplete() } diff --git a/Session/Home/HomeViewModel.swift b/Session/Home/HomeViewModel.swift index a1ff37bfd..8a6022005 100644 --- a/Session/Home/HomeViewModel.swift +++ b/Session/Home/HomeViewModel.swift @@ -268,6 +268,7 @@ public class HomeViewModel { SectionModel( section: .threads, elements: data + .filter { $0.id != SessionThreadViewModel.invalidId } .sorted { lhs, rhs -> Bool in if lhs.threadIsPinned && !rhs.threadIsPinned { return true } if !lhs.threadIsPinned && rhs.threadIsPinned { return false } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index f090f7728..045566300 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -102,12 +102,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD name: .registrationStateDidChange, object: nil ) - NotificationCenter.default.addObserver( - self, - selector: #selector(handleDataNukeRequested), // TODO: This differently??? - name: .dataNukeRequested, - object: nil - ) NotificationCenter.default.addObserver( self, selector: #selector(showMissedCallTipsIfNeeded(_:)), @@ -455,35 +449,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - Notification Handling @objc private func registrationStateDidChange() { - enableBackgroundRefreshIfNecessary() - - guard Identity.userExists() else { return } - - startPollersIfNeeded() - } - - @objc public func handleDataNukeRequested() { - let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs] - let maybeDeviceToken: String? = UserDefaults.standard[.deviceToken] - // TODO: Clean up how this works - if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken { - let data: Data = Data(hex: deviceToken) - PushNotificationAPI.unregister(data).retainUntilComplete() - } - - GRDBStorage.shared.write { db in - _ = try SessionThread.deleteAll(db) - _ = try Identity.deleteAll(db) - } - - SnodeAPI.clearSnodePool() - stopPollers() - - let wasUnlinked: Bool = UserDefaults.standard[.wasUnlinked] - SessionApp.resetAppData { - // Resetting the data clears the old user defaults. We need to restore the unlink default. - UserDefaults.standard[.wasUnlinked] = wasUnlinked - } + handleActivation() } @objc public func showMissedCallTipsIfNeeded(_ notification: Notification) { diff --git a/Session/Meta/SessionApp.swift b/Session/Meta/SessionApp.swift index c223987b2..dadebd048 100644 --- a/Session/Meta/SessionApp.swift +++ b/Session/Meta/SessionApp.swift @@ -63,6 +63,7 @@ public struct SessionApp { GRDBStorage.resetAllStorage() ProfileManager.resetProfileStorage() + Attachment.resetAttachmentStorage() AppEnvironment.shared.notificationPresenter.clearAllNotifications() onReset?() diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index 9eb1c3640..3ebda7272 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Fehler"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 60cee62fc..f46ff4cbf 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index 9ffdb9505..6c0ce3a4f 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Fallo"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index 0c0b2a806..cf91d20ac 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "خطاء"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index 057b87804..c30b60393 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index 42a04ad7a..820120d7c 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Erreur"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index 6cb8421e2..e862c25c0 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index 1ea6f38fa..e40b5492e 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index d46d1a539..3ffd04b51 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Galat"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index d3d10bec1..bbd0183d8 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Errore"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index 2e984b53e..108805053 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "エラー"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index d577ac53e..6aadc1caf 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index 4b8359749..2e93d89d6 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Błąd"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index 38089a102..3a933083a 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Erro"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index 8918c45ec..e6f0421e3 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Ошибка"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index 92bdd53c6..04d1ea3d2 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 9341ea3a5..d2ed3836a 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index 2fa616d8c..617b930c5 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index c51a61b79..5832a403b 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index 185293321..5835cc170 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index f5cb6a74a..333724265 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "Error"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index 62f75a4ec..637d27c67 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -656,3 +656,7 @@ "ALERT_ERROR_TITLE" = "错误"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; "DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"CHATS_TITLE" = "Chats"; +"MESSAGE_TRIMMING_TITLE" = "Message Trimming"; +"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; +"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; diff --git a/Session/Settings/ChatSettingsViewController.swift b/Session/Settings/ChatSettingsViewController.swift new file mode 100644 index 000000000..1e013aea6 --- /dev/null +++ b/Session/Settings/ChatSettingsViewController.swift @@ -0,0 +1,63 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import UIKit +import SessionUIKit +import SignalUtilitiesKit + +// FIXME: Refactor to be MVVM and use database observation +class ChatSettingsViewController: OWSTableViewController { + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + + self.updateTableContents() + + ViewControllerUtilities.setUpDefaultSessionStyle(for: self, title: "CHATS_TITLE".localized(), hasCustomBackButton: false) + + let closeButton: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "X"), style: .plain, target: self, action: #selector(close(_:))) + self.navigationItem.leftBarButtonItem = closeButton + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.updateTableContents() + } + + // MARK: - Table Contents + + func updateTableContents() { + let updatedContents: OWSTableContents = OWSTableContents() + + let messageTrimming: OWSTableSection = OWSTableSection() + messageTrimming.headerTitle = "MESSAGE_TRIMMING_TITLE".localized() + messageTrimming.footerTitle = "MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION".localized() + messageTrimming.add(OWSTableItem.switch( + withText: "MESSAGE_TRIMMING_OPEN_GROUP_TITLE".localized(), + isOn: { GRDBStorage.shared[.trimOpenGroupMessagesOlderThanSixMonths] }, + target: self, + selector: #selector(didToggleTrimOpenGroupsSwitch(_:)) + )) + updatedContents.addSection(messageTrimming) + + self.contents = updatedContents + } + + // MARK: - Actions + + @objc private func didToggleTrimOpenGroupsSwitch(_ sender: UISwitch) { + GRDBStorage.shared.writeAsync( + updates: { db in + db[.trimOpenGroupMessagesOlderThanSixMonths] = !sender.isOn + }, + completion: { [weak self] _, _ in + self?.updateTableContents() + } + ) + } + + @objc private func close(_ sender: UIBarButtonItem) { + self.navigationController?.dismiss(animated: true) + } +} diff --git a/Session/Settings/NukeDataModal.swift b/Session/Settings/NukeDataModal.swift index 06a35c38c..823c7f474 100644 --- a/Session/Settings/NukeDataModal.swift +++ b/Session/Settings/NukeDataModal.swift @@ -157,11 +157,8 @@ final class NukeDataModal: Modal { GRDBStorage.shared .writeAsync { db in try MessageSender.syncConfiguration(db, forceSyncNow: true) } .ensure(on: DispatchQueue.main) { + self?.deleteAllLocalData() self?.dismiss(animated: true, completion: nil) // Dismiss the loader - - UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later - General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access - NotificationCenter.default.post(name: .dataNukeRequested, object: nil) } .retainUntilComplete() } @@ -177,9 +174,7 @@ final class NukeDataModal: Modal { let potentiallyMaliciousSnodes = confirmations.compactMap { $0.value == false ? $0.key : nil } if potentiallyMaliciousSnodes.isEmpty { - General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access - UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later - NotificationCenter.default.post(name: .dataNukeRequested, object: nil) + self?.deleteAllLocalData() } else { let message: String @@ -205,4 +200,36 @@ final class NukeDataModal: Modal { } } } + + private func deleteAllLocalData() { + // Unregister push notifications if needed + let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs] + let maybeDeviceToken: String? = UserDefaults.standard[.deviceToken] + + if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken { + let data: Data = Data(hex: deviceToken) + PushNotificationAPI.unregister(data).retainUntilComplete() + } + + // Clear out the user defaults + UserDefaults.removeAll() + + // Remove the cached key so it gets re-cached on next access + General.cache.mutate { $0.encodedPublicKey = nil } + + // Clear the Snode pool + SnodeAPI.clearSnodePool() + + // Stop any pollers + (UIApplication.shared.delegate as? AppDelegate)?.stopPollers() + + // Call through to the SessionApp's "resetAppData" which will wipe out logs, database and + // profile storage + let wasUnlinked: Bool = UserDefaults.standard[.wasUnlinked] + + SessionApp.resetAppData { + // Resetting the data clears the old user defaults. We need to restore the unlink default. + UserDefaults.standard[.wasUnlinked] = wasUnlinked + } + } } diff --git a/Session/Settings/SettingsVC.swift b/Session/Settings/SettingsVC.swift index 2cd3a4799..56e8b4500 100644 --- a/Session/Settings/SettingsVC.swift +++ b/Session/Settings/SettingsVC.swift @@ -322,6 +322,8 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate { getSeparator(), getSettingButton(withTitle: NSLocalizedString("MESSAGE_REQUESTS_TITLE", comment: ""), color: Colors.text, action: #selector(showMessageRequests)), getSeparator(), + getSettingButton(withTitle: NSLocalizedString("CHATS_TITLE", comment: ""), color: Colors.text, action: #selector(showChatSettings)), + getSeparator(), getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)), getSeparator(), getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)), @@ -629,6 +631,11 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate { self.navigationController?.pushViewController(viewController, animated: true) } + @objc private func showChatSettings() { + let chatSettingsVC = ChatSettingsViewController() + navigationController!.pushViewController(chatSettingsVC, animated: true) + } + @objc private func showSeed() { let seedModal = SeedModal() seedModal.modalPresentationStyle = .overFullScreen diff --git a/Session/Shared/FullConversationCell.swift b/Session/Shared/FullConversationCell.swift index fa9973805..e8beb16b1 100644 --- a/Session/Shared/FullConversationCell.swift +++ b/Session/Shared/FullConversationCell.swift @@ -433,6 +433,7 @@ public final class FullConversationCell: UITableViewCell { in: Interaction.previewText( variant: (cellViewModel.interactionVariant ?? .standardIncoming), body: cellViewModel.interactionBody, + threadContactDisplayName: cellViewModel.threadContactName(), authorDisplayName: cellViewModel.authorName(for: cellViewModel.threadVariant), attachmentDescriptionInfo: cellViewModel.interactionAttachmentDescriptionInfo, attachmentCount: cellViewModel.interactionAttachmentCount, diff --git a/SessionMessagingKit/Calls/WebRTCSession.swift b/SessionMessagingKit/Calls/WebRTCSession.swift index 20345393c..3f82bda0c 100644 --- a/SessionMessagingKit/Calls/WebRTCSession.swift +++ b/SessionMessagingKit/Calls/WebRTCSession.swift @@ -169,6 +169,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { return seal.reject(error) } } + GRDBStorage.shared .writeAsync { db in try MessageSender diff --git a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift index 616038fde..ac82f040a 100644 --- a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -48,7 +48,7 @@ enum _002_SetupStandardJobs: Migration { _ = try Job( variant: .garbageCollection, - behaviour: .recurringOnLaunch, + behaviour: .recurringOnActive, details: GarbageCollectionJob.Details( typesToCollect: GarbageCollectionJob.Types.allCases ) diff --git a/SessionMessagingKit/Database/Models/Attachment.swift b/SessionMessagingKit/Database/Models/Attachment.swift index 7b69b5e19..c04ea6aef 100644 --- a/SessionMessagingKit/Database/Models/Attachment.swift +++ b/SessionMessagingKit/Database/Models/Attachment.swift @@ -263,6 +263,7 @@ extension Attachment: CustomStringConvertible { // We only support multi-attachment sending of images so we can just default to the image attachment // if there were multiple attachments guard count == 1 else { return "\(emoji(for: OWSMimeTypeImageJpeg)) \("ATTACHMENT".localized())" } + if MIMETypeUtil.isAudio(descriptionInfo.contentType) { // a missing filename is the legacy way to determine if an audio attachment is // a voice note vs. other arbitrary audio attachments. @@ -583,12 +584,9 @@ extension Attachment { return attachmentsFolder }() - private static var thumbnailsFolder: String = { - let attachmentsFolder: String = sharedDataAttachmentsDirPath - OWSFileSystem.ensureDirectoryExists(attachmentsFolder) - - return attachmentsFolder - }() + public static func resetAttachmentStorage() { + try? FileManager.default.removeItem(atPath: Attachment.sharedDataAttachmentsDirPath) + } public static func originalFilePath(id: String, mimeType: String, sourceFilename: String?) -> String? { return MIMETypeUtil.filePath( diff --git a/SessionMessagingKit/Database/Models/BlindedIdLookup.swift b/SessionMessagingKit/Database/Models/BlindedIdLookup.swift index 79d033931..6d63624ae 100644 --- a/SessionMessagingKit/Database/Models/BlindedIdLookup.swift +++ b/SessionMessagingKit/Database/Models/BlindedIdLookup.swift @@ -74,6 +74,7 @@ public extension BlindedIdLookup { blindedId: String, openGroupServer: String, openGroupPublicKey: String, + isCheckingForOutbox: Bool, dependencies: SMKDependencies = SMKDependencies() ) throws -> BlindedIdLookup { var lookup: BlindedIdLookup = (try? BlindedIdLookup @@ -92,11 +93,11 @@ public extension BlindedIdLookup { // We now need to try to match the blinded id to an existing contact, this can only be done by looping // through all approved contacts and generating a blinded id for the provided open group for each to // see if it matches the provided blindedId - let approvedContactCursor: RecordCursor<Contact> = try Contact - .filter(Contact.Columns.isApproved == true) + let contactsThatApprovedMeCursor: RecordCursor<Contact> = try Contact + .filter(Contact.Columns.didApproveMe == true) .fetchCursor(db) - - while let contact: Contact = try approvedContactCursor.next() { + + while let contact: Contact = try contactsThatApprovedMeCursor.next() { guard dependencies.sodium.sessionId(contact.id, matchesBlindedId: blindedId, serverPublicKey: openGroupPublicKey, genericHash: dependencies.genericHash) else { continue } @@ -105,6 +106,16 @@ public extension BlindedIdLookup { lookup = try lookup .with(sessionId: contact.id) .saved(db) + + // There is an edge-case where the contact might not have their 'isApproved' flag set to true + // but if we have a `BlindedIdLookup` for them and are performing the lookup from the outbox + // then that means we sent them a message request and the 'isApproved' flag should be true + if isCheckingForOutbox && !contact.isApproved { + try Contact + .filter(id: contact.id) + .updateAll(db, Contact.Columns.isApproved.set(to: true)) + } + break } diff --git a/SessionMessagingKit/Database/Models/Interaction.swift b/SessionMessagingKit/Database/Models/Interaction.swift index d082e108d..238816699 100644 --- a/SessionMessagingKit/Database/Models/Interaction.swift +++ b/SessionMessagingKit/Database/Models/Interaction.swift @@ -690,6 +690,7 @@ public extension Interaction { static func previewText( variant: Variant, body: String?, + threadContactDisplayName: String = "", authorDisplayName: String = "", attachmentDescriptionInfo: Attachment.DescriptionInfo? = nil, attachmentCount: Int? = nil, @@ -764,7 +765,7 @@ public extension Interaction { ) else { return (body ?? "") } - return messageInfo.previewText(authorDisplayName: authorDisplayName) + return messageInfo.previewText(threadContactDisplayName: threadContactDisplayName) } } diff --git a/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift b/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift index 42c8de672..78225cf0c 100644 --- a/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift +++ b/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift @@ -52,7 +52,7 @@ public enum GarbageCollectionJob: JobExecutor { } /// Remove any old open group messages - open group messages which are older than six months - if details.typesToCollect.contains(.oldOpenGroupMessages) { + if details.typesToCollect.contains(.oldOpenGroupMessages) && db[.trimOpenGroupMessagesOlderThanSixMonths] { let interaction: TypedTableAlias<Interaction> = TypedTableAlias() let thread: TypedTableAlias<SessionThread> = TypedTableAlias() diff --git a/SessionMessagingKit/Messages/Control Messages/CallMessage.swift b/SessionMessagingKit/Messages/Control Messages/CallMessage.swift index e53aa06fc..533f771ba 100644 --- a/SessionMessagingKit/Messages/Control Messages/CallMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/CallMessage.swift @@ -235,24 +235,24 @@ public extension CallMessage { // MARK: - Content - func previewText(authorDisplayName: String) -> String { + func previewText(threadContactDisplayName: String) -> String { switch state { case .incoming: return String( format: "call_incoming".localized(), - authorDisplayName + threadContactDisplayName ) case .outgoing: return String( format: "call_outgoing".localized(), - authorDisplayName + threadContactDisplayName ) case .missed, .permissionDenied: return String( format: "call_missed".localized(), - authorDisplayName + threadContactDisplayName ) // TODO: We should do better here diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index 94a9d099b..57da8bfd3 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -582,7 +582,9 @@ public final class OpenGroupManager: NSObject { db, blindedId: message.recipient, openGroupServer: server.lowercased(), - openGroupPublicKey: openGroup.publicKey + openGroupPublicKey: openGroup.publicKey, + isCheckingForOutbox: true, + dependencies: dependencies ) }() let syncTarget: String = (lookup.sessionId ?? message.recipient) diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ConfigurationMessages.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ConfigurationMessages.swift index 9fc4b1ce5..1259720ae 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ConfigurationMessages.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ConfigurationMessages.swift @@ -51,6 +51,20 @@ extension MessageReceiver { try message.contacts.forEach { contactInfo in guard let sessionId: String = contactInfo.publicKey else { return } + // If the contact is a blinded contact then only add them if they haven't already been + // unblinded + if SessionId.Prefix(from: sessionId) == .blinded { + let hasUnblindedContact: Bool = (try? BlindedIdLookup + .filter(BlindedIdLookup.Columns.blindedId == sessionId) + .filter(BlindedIdLookup.Columns.sessionId != nil) + .isNotEmpty(db)) + .defaulting(to: false) + + if hasUnblindedContact { + return + } + } + // Note: We only update the contact and profile records if the data has actually changed // in order to avoid triggering UI updates for every thread on the home screen let contact: Contact = Contact.fetchOrCreate(db, id: sessionId) diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index a1c045c46..f470754c0 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -233,10 +233,18 @@ public final class MessageSender { isSyncMessage: isSyncMessage ) - let shouldNotify = ( - (message is VisibleMessage || message is UnsendRequest) && - !isSyncMessage - ) + let shouldNotify: Bool = { + switch message { + case is VisibleMessage, is UnsendRequest: return !isSyncMessage + case let callMessage as CallMessage: + switch callMessage.kind { + case .preOffer: return true + default: return false + } + + default: return false + } + }() /* if let closedGroupControlMessage = message as? ClosedGroupControlMessage, case .new = closedGroupControlMessage.kind { diff --git a/SessionMessagingKit/Shared Models/MessageViewModel.swift b/SessionMessagingKit/Shared Models/MessageViewModel.swift index 2756a29a7..363e98069 100644 --- a/SessionMessagingKit/Shared Models/MessageViewModel.swift +++ b/SessionMessagingKit/Shared Models/MessageViewModel.swift @@ -10,11 +10,13 @@ fileprivate typealias AttachmentInteractionInfo = MessageViewModel.AttachmentInt fileprivate typealias TypingIndicatorInfo = MessageViewModel.TypingIndicatorInfo public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable, Hashable, Identifiable, Differentiable { + public static let threadIdKey: SQL = SQL(stringLiteral: CodingKeys.threadId.stringValue) public static let threadVariantKey: SQL = SQL(stringLiteral: CodingKeys.threadVariant.stringValue) public static let threadIsTrustedKey: SQL = SQL(stringLiteral: CodingKeys.threadIsTrusted.stringValue) public static let threadHasDisappearingMessagesEnabledKey: SQL = SQL(stringLiteral: CodingKeys.threadHasDisappearingMessagesEnabled.stringValue) public static let threadOpenGroupServerKey: SQL = SQL(stringLiteral: CodingKeys.threadOpenGroupServer.stringValue) public static let threadOpenGroupPublicKeyKey: SQL = SQL(stringLiteral: CodingKeys.threadOpenGroupPublicKey.stringValue) + public static let threadContactNameInternalKey: SQL = SQL(stringLiteral: CodingKeys.threadContactNameInternal.stringValue) public static let rowIdKey: SQL = SQL(stringLiteral: CodingKeys.rowId.stringValue) public static let authorNameInternalKey: SQL = SQL(stringLiteral: CodingKeys.authorNameInternal.stringValue) public static let stateKey: SQL = SQL(stringLiteral: CodingKeys.state.stringValue) @@ -58,11 +60,13 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable, // Thread Info + public let threadId: String public let threadVariant: SessionThread.Variant public let threadIsTrusted: Bool public let threadHasDisappearingMessagesEnabled: Bool public let threadOpenGroupServer: String? public let threadOpenGroupPublicKey: String? + private let threadContactNameInternal: String? // Interaction Info @@ -133,11 +137,13 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable, public func with(attachments: [Attachment]) -> MessageViewModel { return MessageViewModel( + threadId: self.threadId, threadVariant: self.threadVariant, threadIsTrusted: self.threadIsTrusted, threadHasDisappearingMessagesEnabled: self.threadHasDisappearingMessagesEnabled, threadOpenGroupServer: self.threadOpenGroupServer, threadOpenGroupPublicKey: self.threadOpenGroupPublicKey, + threadContactNameInternal: self.threadContactNameInternal, rowId: self.rowId, id: self.id, variant: self.variant, @@ -281,11 +287,13 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable, }() return ViewModel( + threadId: self.threadId, threadVariant: self.threadVariant, threadIsTrusted: self.threadIsTrusted, threadHasDisappearingMessagesEnabled: self.threadHasDisappearingMessagesEnabled, threadOpenGroupServer: self.threadOpenGroupServer, threadOpenGroupPublicKey: self.threadOpenGroupPublicKey, + threadContactNameInternal: self.threadContactNameInternal, rowId: self.rowId, id: self.id, variant: self.variant, @@ -298,6 +306,12 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable, Interaction.previewText( variant: self.variant, body: self.body, + threadContactDisplayName: Profile.displayName( + for: self.threadVariant, + id: self.threadId, + name: self.threadContactNameInternal, + nickname: nil // Folded into 'threadContactNameInternal' within the Query + ), authorDisplayName: authorDisplayName, attachmentDescriptionInfo: self.attachments?.first.map { firstAttachment in Attachment.DescriptionInfo( @@ -428,11 +442,13 @@ public extension MessageViewModel { // Note: This init method is only used system-created cells or empty states init(isTypingIndicator: Bool? = nil) { + self.threadId = "INVALID_THREAD_ID" self.threadVariant = .contact self.threadIsTrusted = false self.threadHasDisappearingMessagesEnabled = false self.threadOpenGroupServer = nil self.threadOpenGroupPublicKey = nil + self.threadContactNameInternal = nil // Interaction Info @@ -552,6 +568,10 @@ public extension MessageViewModel { let quote: TypedTableAlias<Quote> = TypedTableAlias() let linkPreview: TypedTableAlias<LinkPreview> = TypedTableAlias() + let threadProfileTableLiteral: SQL = SQL(stringLiteral: "threadProfile") + let profileIdColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.id.name) + let profileNicknameColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.nickname.name) + let profileNameColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.name.name) let interactionStateInteractionIdColumnLiteral: SQL = SQL(stringLiteral: RecipientState.Columns.interactionId.name) let readReceiptTableLiteral: SQL = SQL(stringLiteral: "readReceipt") let readReceiptReadTimestampMsColumnLiteral: SQL = SQL(stringLiteral: RecipientState.Columns.readTimestampMs.name) @@ -561,9 +581,10 @@ public extension MessageViewModel { let groupMemberProfileIdColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.profileId.name) let groupMemberRoleColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.role.name) - let numColumnsBeforeLinkedRecords: Int = 18 + let numColumnsBeforeLinkedRecords: Int = 20 let request: SQLRequest<ViewModel> = """ SELECT + \(thread[.id]) AS \(ViewModel.threadIdKey), \(thread[.variant]) AS \(ViewModel.threadVariantKey), -- Default to 'true' for non-contact threads IFNULL(\(contact[.isTrusted]), true) AS \(ViewModel.threadIsTrustedKey), @@ -571,6 +592,7 @@ public extension MessageViewModel { IFNULL(\(disappearingMessagesConfig[.isEnabled]), false) AS \(ViewModel.threadHasDisappearingMessagesEnabledKey), \(openGroup[.server]) AS \(ViewModel.threadOpenGroupServerKey), \(openGroup[.publicKey]) AS \(ViewModel.threadOpenGroupPublicKeyKey), + IFNULL(\(threadProfileTableLiteral).\(profileNicknameColumnLiteral), \(threadProfileTableLiteral).\(profileNameColumnLiteral)) AS \(ViewModel.threadContactNameInternalKey), \(interaction.alias[Column.rowID]) AS \(ViewModel.rowIdKey), \(interaction[.id]), @@ -610,6 +632,7 @@ public extension MessageViewModel { FROM \(Interaction.self) JOIN \(SessionThread.self) ON \(thread[.id]) = \(interaction[.threadId]) LEFT JOIN \(Contact.self) ON \(contact[.id]) = \(interaction[.threadId]) + LEFT JOIN \(Profile.self) AS \(threadProfileTableLiteral) ON \(threadProfileTableLiteral).\(profileIdColumnLiteral) = \(interaction[.threadId]) LEFT JOIN \(DisappearingMessagesConfiguration.self) ON \(disappearingMessagesConfig[.threadId]) = \(interaction[.threadId]) LEFT JOIN \(OpenGroup.self) ON \(openGroup[.threadId]) = \(interaction[.threadId]) LEFT JOIN \(Profile.self) ON \(profile[.id]) = \(interaction[.authorId]) diff --git a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift index d4f7083d3..e9132b51d 100644 --- a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift +++ b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift @@ -52,6 +52,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat public static let interactionIsOpenGroupInvitationKey: SQL = SQL(stringLiteral: CodingKeys.interactionIsOpenGroupInvitation.stringValue) public static let interactionAttachmentDescriptionInfoKey: SQL = SQL(stringLiteral: CodingKeys.interactionAttachmentDescriptionInfo.stringValue) public static let interactionAttachmentCountKey: SQL = SQL(stringLiteral: CodingKeys.interactionAttachmentCount.stringValue) + public static let threadContactNameInternalKey: SQL = SQL(stringLiteral: CodingKeys.threadContactNameInternal.stringValue) public static let authorNameInternalKey: SQL = SQL(stringLiteral: CodingKeys.authorNameInternal.stringValue) public static let currentUserPublicKeyKey: SQL = SQL(stringLiteral: CodingKeys.currentUserPublicKey.stringValue) @@ -75,11 +76,15 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat public let threadMemberNames: String? public let threadIsNoteToSelf: Bool - public var threadIsMessageRequest: Bool? + + /// This flag indicates whether the thread is an outgoing message request + public let threadIsMessageRequest: Bool? + + /// This flag indicates whether the thread is an incoming message request public let threadRequiresApproval: Bool? public let threadShouldBeVisible: Bool? public let threadIsPinned: Bool - public var threadIsBlocked: Bool? + public let threadIsBlocked: Bool? public let threadMutedUntilTimestamp: TimeInterval? public let threadOnlyNotifyForMentions: Bool? public let threadMessageDraft: String? @@ -116,6 +121,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat public let interactionAttachmentCount: Int? public let authorId: String? + private let threadContactNameInternal: String? private let authorNameInternal: String? public let currentUserPublicKey: String @@ -172,6 +178,21 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat } } + /// This function returns the thread contact profile name formatted for the specific type of thread provided + /// + /// **Note:** The 'threadVariant' parameter is used for profile context but in the search results we actually want this + /// to always behave as the `contact` variant which is why this needs to be a function instead of just using the provided + /// parameter + public func threadContactName() -> String { + return Profile.displayName( + for: .contact, + id: threadId, + name: threadContactNameInternal, + nickname: nil, // Folded into 'threadContactNameInternal' within the Query + customFallback: "Anonymous" + ) + } + /// This function returns the profile name formatted for the specific type of thread provided /// /// **Note:** The 'threadVariant' parameter is used for profile context but in the search results we actually want this @@ -251,6 +272,7 @@ public extension SessionThreadViewModel { self.interactionAttachmentCount = nil self.authorId = nil + self.threadContactNameInternal = nil self.authorNameInternal = nil self.currentUserPublicKey = getUserHexEncodedPublicKey() } @@ -282,6 +304,8 @@ public extension SessionThreadViewModel { let profile: TypedTableAlias<Profile> = TypedTableAlias() let profileIdColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.id.name) + let profileNicknameColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.nickname.name) + let profileNameColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.name.name) let firstInteractionAttachmentLiteral: SQL = SQL(stringLiteral: "firstInteractionAttachment") let interactionAttachmentAttachmentIdColumnLiteral: SQL = SQL(stringLiteral: InteractionAttachment.Columns.attachmentId.name) let interactionAttachmentInteractionIdColumnLiteral: SQL = SQL(stringLiteral: InteractionAttachment.Columns.interactionId.name) @@ -338,6 +362,7 @@ public extension SessionThreadViewModel { COUNT(\(interactionAttachment[.interactionId])) AS \(ViewModel.interactionAttachmentCountKey), \(interaction[.authorId]), + IFNULL(\(ViewModel.contactProfileKey).\(profileNicknameColumnLiteral), \(ViewModel.contactProfileKey).\(profileNameColumnLiteral)) AS \(ViewModel.threadContactNameInternalKey), IFNULL(\(profile[.nickname]), \(profile[.name])) AS \(ViewModel.authorNameInternalKey), \(SQL("\(userPublicKey)")) AS \(ViewModel.currentUserPublicKeyKey) @@ -549,11 +574,8 @@ public extension SessionThreadViewModel { ( \(thread[.shouldBeVisible]) = true AND \(SQL("\(thread[.variant]) = \(SessionThread.Variant.contact)")) AND - \(SQL("\(thread[.id]) != \(userPublicKey)")) AND ( - -- A '!= true' check doesn't work properly so we need to be explicit - \(contact[.isApproved]) IS NULL OR - \(contact[.isApproved]) = false - ) + \(SQL("\(thread[.id]) != \(userPublicKey)")) AND + IFNULL(\(contact[.isApproved]), false) = false ) AS \(ViewModel.threadIsMessageRequestKey), ( \(SQL("\(thread[.variant]) = \(SessionThread.Variant.contact)")) AND ( diff --git a/SessionMessagingKit/Utilities/Preferences.swift b/SessionMessagingKit/Utilities/Preferences.swift index 243efec10..414417396 100644 --- a/SessionMessagingKit/Utilities/Preferences.swift +++ b/SessionMessagingKit/Utilities/Preferences.swift @@ -41,6 +41,9 @@ public extension Setting.BoolKey { /// Controls whether Calls are enabled static let areCallsEnabled: Setting.BoolKey = "areCallsEnabled" + /// Controls whether open group messages older than 6 months should be deleted + static let trimOpenGroupMessagesOlderThanSixMonths: Setting.BoolKey = "trimOpenGroupMessagesOlderThanSixMonths" + /// Controls whether the message requests item has been hidden on the home screen static let hasHiddenMessageRequests: Setting.BoolKey = "hasHiddenMessageRequests" diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 5d831d2d6..66cee426b 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -104,7 +104,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension guard case .preOffer = callMessage.kind else { return self.completeSilenty() } - if db[.areCallsEnabled] { + if !db[.areCallsEnabled] { if let sender: String = callMessage.sender, let interaction: Interaction = try MessageReceiver.insertCallInfoMessage(db, for: callMessage, state: .permissionDenied) { let thread: SessionThread = try SessionThread.fetchOrCreate(db, id: sender, variant: .contact) diff --git a/SessionUtilitiesKit/Database/GRDBStorage.swift b/SessionUtilitiesKit/Database/GRDBStorage.swift index 3d59ac1e6..ddda89bef 100644 --- a/SessionUtilitiesKit/Database/GRDBStorage.swift +++ b/SessionUtilitiesKit/Database/GRDBStorage.swift @@ -280,18 +280,16 @@ public final class GRDBStorage { // MARK: - File Management public static func resetAllStorage() { - NotificationCenter.default.post(name: .resetStorage, object: nil) + // Just in case they haven't been removed for some reason, delete the legacy database & keys + SUKLegacy.clearLegacyDatabaseInstance() + try? SUKLegacy.deleteLegacyDatabaseFilesAndKey() + + GRDBStorage.shared.isValid = false + GRDBStorage.shared.hasCompletedMigrations = false + GRDBStorage.shared.dbWriter = nil - // This might be redundant but in the spirit of thoroughness... self.deleteDatabaseFiles() - try? self.deleteDbKeys() - - if CurrentAppContext().isMainApp { -// TSAttachmentStream.deleteAttachments() - } - - // TODO: Delete Profiles on Disk? } public/*private*/ static func deleteDatabaseFiles() { diff --git a/SessionUtilitiesKit/Database/Types/PagedDatabaseObserver.swift b/SessionUtilitiesKit/Database/Types/PagedDatabaseObserver.swift index d011ca025..561bb409e 100644 --- a/SessionUtilitiesKit/Database/Types/PagedDatabaseObserver.swift +++ b/SessionUtilitiesKit/Database/Types/PagedDatabaseObserver.swift @@ -254,7 +254,7 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where } // Fetch the indexes of the rowIds so we can determine whether they should be added to the screen - let itemIndexes: [Int64] = PagedData.indexes( + let itemIndexes: [PagedData.RowIndexInfo] = PagedData.indexes( db, rowIds: changesToQuery.map { $0.rowId }, tableName: pagedTableName, @@ -262,7 +262,7 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where orderSQL: orderSQL, filterSQL: filterSQL ) - let relatedChangeIndexes: [Int64] = PagedData.indexes( + let relatedChangeIndexes: [PagedData.RowIndexInfo] = PagedData.indexes( db, rowIds: Array(pagedRowIdsForRelatedChanges), tableName: pagedTableName, @@ -275,36 +275,34 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where // which shouldn't - values less than 'currentCount' or if there is at least one value less than // 'currentCount' and the indexes are sequential (ie. more than the current loaded content was // added at once) - func determineValidChanges<T>(for indexes: [Int64], with data: [T]) -> [T] { + func determineValidChanges(for indexInfo: [PagedData.RowIndexInfo]) -> [Int64] { + let indexes: [Int64] = Array(indexInfo + .map { $0.rowIndex } + .sorted() + .asSet()) let indexesAreSequential: Bool = (indexes.map { $0 - 1 }.dropFirst() == indexes.dropLast()) - let hasOneValidIndex: Bool = indexes.contains(where: { index -> Bool in - index >= updatedPageInfo.pageOffset && ( - index < updatedPageInfo.currentCount || + let hasOneValidIndex: Bool = indexInfo.contains(where: { info -> Bool in + info.rowIndex >= updatedPageInfo.pageOffset && ( + info.rowIndex < updatedPageInfo.currentCount || updatedPageInfo.currentCount == 0 ) }) return (indexesAreSequential && hasOneValidIndex ? - data : - zip(indexes, data) - .filter { index, _ -> Bool in - index >= updatedPageInfo.pageOffset && ( - index < updatedPageInfo.currentCount || + indexInfo.map { $0.rowId } : + indexInfo + .filter { info -> Bool in + info.rowIndex >= updatedPageInfo.pageOffset && ( + info.rowIndex < updatedPageInfo.currentCount || updatedPageInfo.currentCount == 0 ) } - .map { _, value -> T in value } + .map { info -> Int64 in info.rowId } ) } - let validChanges: [PagedData.TrackedChange] = determineValidChanges( - for: itemIndexes, - with: changesToQuery - ) - let validRelatedChangeRowIds: [Int64] = determineValidChanges( - for: relatedChangeIndexes, - with: Array(pagedRowIdsForRelatedChanges) - ) - let countBefore: Int = itemIndexes.filter { $0 < updatedPageInfo.pageOffset }.count + let validChangeRowIds: [Int64] = determineValidChanges(for: itemIndexes) + let validRelatedChangeRowIds: [Int64] = determineValidChanges(for: relatedChangeIndexes) + let countBefore: Int = itemIndexes.filter { $0.rowIndex < updatedPageInfo.pageOffset }.count // Update the offset and totalCount even if the rows are outside of the current page (need to // in order to ensure the 'load more' sections are accurate) @@ -312,18 +310,24 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where pageSize: updatedPageInfo.pageSize, pageOffset: (updatedPageInfo.pageOffset + countBefore), currentCount: updatedPageInfo.currentCount, - totalCount: (updatedPageInfo.totalCount + validChanges.filter { $0.kind == .insert }.count) + totalCount: ( + updatedPageInfo.totalCount + + changesToQuery + .filter { $0.kind == .insert } + .filter { validChangeRowIds.contains($0.rowId) } + .count + ) ) // If there are no valid row ids then stop here (trigger updates though since the page info // has changes) - guard !validChanges.isEmpty || !validRelatedChangeRowIds.isEmpty else { + guard !validChangeRowIds.isEmpty || !validRelatedChangeRowIds.isEmpty else { updateDataAndCallbackIfNeeded(updatedDataCache, updatedPageInfo, true) return } // Fetch the inserted/updated rows - let targetRowIds: [Int64] = Array((validChanges.map { $0.rowId } + validRelatedChangeRowIds).asSet()) + let targetRowIds: [Int64] = Array((validChangeRowIds + validRelatedChangeRowIds).asSet()) let updatedItems: [T] = (try? dataQuery(targetRowIds) .fetchAll(db)) .defaulting(to: []) @@ -808,6 +812,11 @@ public enum PagedData { } } + fileprivate struct RowIndexInfo: Decodable, FetchableRecord { + let rowId: Int64 + let rowIndex: Int64 + } + // MARK: - Internal Functions fileprivate static func totalCount( @@ -891,12 +900,13 @@ public enum PagedData { requiredJoinSQL: SQL? = nil, orderSQL: SQL, filterSQL: SQL - ) -> [Int64] { + ) -> [RowIndexInfo] { guard !rowIds.isEmpty else { return [] } let tableNameLiteral: SQL = SQL(stringLiteral: tableName) - let request: SQLRequest<Int64> = """ + let request: SQLRequest<RowIndexInfo> = """ SELECT + data.rowId AS rowId, (data.rowIndex - 1) AS rowIndex -- Converting from 1-Indexed to 0-indexed FROM ( SELECT @@ -1057,7 +1067,7 @@ public class AssociatedRecord<T, PagedType>: ErasedAssociatedRecord where T: Fet // If the associated data change isn't related to the paged type then no need to continue guard !pagedRowIds.isEmpty else { return (oldCount != countAfterDeletions) } - let pagedItemIndexes: [Int64] = PagedData.indexes( + let pagedItemIndexes: [PagedData.RowIndexInfo] = PagedData.indexes( db, rowIds: pagedRowIds, tableName: pagedTableName, @@ -1078,9 +1088,9 @@ public class AssociatedRecord<T, PagedType>: ErasedAssociatedRecord where T: Fet /// Instead of following the pattern the `PagedDatabaseObserver` does where we get the proper `validRowIds` we /// basically have to check if there is a single valid index, and if so retrieve and store all data related to the changes for this /// commit - this will mean in some cases we cache data which is actually unrelated to the filtered paged data - let hasOneValidIndex: Bool = pagedItemIndexes.contains(where: { index -> Bool in - index >= pageInfo.pageOffset && ( - index < pageInfo.currentCount || + let hasOneValidIndex: Bool = pagedItemIndexes.contains(where: { info -> Bool in + info.rowIndex >= pageInfo.pageOffset && ( + info.rowIndex < pageInfo.currentCount || pageInfo.currentCount == 0 ) }) diff --git a/SessionUtilitiesKit/Database/Utilities/GRDB+Notifications.swift b/SessionUtilitiesKit/Database/Utilities/GRDB+Notifications.swift deleted file mode 100644 index fe1d4f95e..000000000 --- a/SessionUtilitiesKit/Database/Utilities/GRDB+Notifications.swift +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -public extension Notification.Name { - static let resetStorage = Notification.Name("resetStorage") -} - -@objc public extension NSNotification { - @objc static let resetStorage = Notification.Name.resetStorage.rawValue as NSString -} diff --git a/SignalUtilitiesKit/Utilities/Notification+Loki.swift b/SignalUtilitiesKit/Utilities/Notification+Loki.swift index 46d08fadf..958e41249 100644 --- a/SignalUtilitiesKit/Utilities/Notification+Loki.swift +++ b/SignalUtilitiesKit/Utilities/Notification+Loki.swift @@ -6,8 +6,6 @@ public extension Notification.Name { static let contactOnlineStatusChanged = Notification.Name("contactOnlineStatusChanged") static let threadDeleted = Notification.Name("threadDeleted") static let threadSessionRestoreDevicesChanged = Notification.Name("threadSessionRestoreDevicesChanged") - // Interaction - static let dataNukeRequested = Notification.Name("dataNukeRequested") } @objc public extension NSNotification { @@ -16,6 +14,4 @@ public extension Notification.Name { @objc static let contactOnlineStatusChanged = Notification.Name.contactOnlineStatusChanged.rawValue as NSString @objc static let threadDeleted = Notification.Name.threadDeleted.rawValue as NSString @objc static let threadSessionRestoreDevicesChanged = Notification.Name.threadSessionRestoreDevicesChanged.rawValue as NSString - // Interaction - @objc static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString }