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:
Morgan Pretty 2022-06-29 18:10:10 +10:00
parent 76f7e4e246
commit c7e8071dd1
51 changed files with 372 additions and 134 deletions

View File

@ -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 */,

View File

@ -942,7 +942,8 @@ extension ConversationVC:
db,
blindedId: sessionId,
openGroupServer: openGroupServer,
openGroupPublicKey: openGroupPublicKey
openGroupPublicKey: openGroupPublicKey,
isCheckingForOutbox: false
)
return try SessionThread

View File

@ -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()
}
}
}

View File

@ -627,6 +627,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
)
)
)
try MessageSender.syncConfiguration(db, forceSyncNow: true)
.retainUntilComplete()
}

View File

@ -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 }

View File

@ -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) {

View File

@ -63,6 +63,7 @@ public struct SessionApp {
GRDBStorage.resetAllStorage()
ProfileManager.resetProfileStorage()
Attachment.resetAttachmentStorage()
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
onReset?()

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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)
}
}

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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,

View File

@ -169,6 +169,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
return seal.reject(error)
}
}
GRDBStorage.shared
.writeAsync { db in
try MessageSender

View File

@ -48,7 +48,7 @@ enum _002_SetupStandardJobs: Migration {
_ = try Job(
variant: .garbageCollection,
behaviour: .recurringOnLaunch,
behaviour: .recurringOnActive,
details: GarbageCollectionJob.Details(
typesToCollect: GarbageCollectionJob.Types.allCases
)

View File

@ -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(

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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])

View File

@ -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 (

View File

@ -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"

View File

@ -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)

View File

@ -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() {

View File

@ -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
)
})

View File

@ -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
}

View File

@ -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
}