Fixed a large number of bugs and added a setting to control open group message deletion
Added a setting to control whether open group messages older than 6 months should be pruned Added some defensive coding to prevent an edge-case which could cause a crash (wasn't filtering out a potential invalid row from the home screen data) Fixed a bug where preOffer call messages weren't correctly sending push notifications Fixed a bug where all incoming calls would be rejected and seen as calls disabled Fixed a bug where the copy on call info messages was displaying the sender's name instead of the thread contact's name for outgoing calls Fixed a bug where the input view wouldn't appear when creating new DM conversations Fixed a bug where threads might not show the message request approval UI Fixed an issue where some logic might not have run correctly when first registering an account Fixed a bug where the note to self thread could incorrectly appear when restoring a device Updated the GarbageCollectionJob to run onActive instead of onLaunch (since it's likely we will rarely launch) Updated the logic for erasing an account from a device
This commit is contained in:
parent
76f7e4e246
commit
c7e8071dd1
|
@ -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 */,
|
||||
|
|
|
@ -942,7 +942,8 @@ extension ConversationVC:
|
|||
db,
|
||||
blindedId: sessionId,
|
||||
openGroupServer: openGroupServer,
|
||||
openGroupPublicKey: openGroupPublicKey
|
||||
openGroupPublicKey: openGroupPublicKey,
|
||||
isCheckingForOutbox: false
|
||||
)
|
||||
|
||||
return try SessionThread
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -627,6 +627,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
|
|||
)
|
||||
)
|
||||
)
|
||||
|
||||
try MessageSender.syncConfiguration(db, forceSyncNow: true)
|
||||
.retainUntilComplete()
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -63,6 +63,7 @@ public struct SessionApp {
|
|||
|
||||
GRDBStorage.resetAllStorage()
|
||||
ProfileManager.resetProfileStorage()
|
||||
Attachment.resetAttachmentStorage()
|
||||
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
|
||||
|
||||
onReset?()
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -169,6 +169,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
return seal.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
GRDBStorage.shared
|
||||
.writeAsync { db in
|
||||
try MessageSender
|
||||
|
|
|
@ -48,7 +48,7 @@ enum _002_SetupStandardJobs: Migration {
|
|||
|
||||
_ = try Job(
|
||||
variant: .garbageCollection,
|
||||
behaviour: .recurringOnLaunch,
|
||||
behaviour: .recurringOnActive,
|
||||
details: GarbageCollectionJob.Details(
|
||||
typesToCollect: GarbageCollectionJob.Types.allCases
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue