From 930d31161f4a05377609c8b3f8b3a8a330a3ef1d Mon Sep 17 00:00:00 2001 From: Vyacheslav Karpukhin Date: Wed, 26 Feb 2020 01:40:20 +0100 Subject: [PATCH 1/2] Add a possibility to delete downloaded media to free up storage --- ChatSecure.xcodeproj/project.pbxproj | 8 + .../Classes/Controllers/OTRMediaFileManager.m | 10 +- .../Classes/Controllers/OTRSettingsManager.m | 7 +- .../Settings/OTRStorageUsageSetting.swift | 12 ++ .../OTRStorageUsageViewController.swift | 195 ++++++++++++++++++ ChatSecureCore/Public/OTRMediaFileManager.h | 4 +- OTRAssets/Strings/OTRStrings.h | 8 + OTRAssets/Strings/OTRStrings.m | 8 + OTRAssets/Strings/strings.json | 12 ++ .../Base.lproj/Localizable.strings | Bin 68784 -> 69462 bytes Submodules/IOCipher | 2 +- Submodules/libsqlfs | 2 +- 12 files changed, 262 insertions(+), 6 deletions(-) create mode 100644 ChatSecureCore/Classes/Model/Settings/OTRStorageUsageSetting.swift create mode 100644 ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift diff --git a/ChatSecure.xcodeproj/project.pbxproj b/ChatSecure.xcodeproj/project.pbxproj index cf9ef4e83..4f5833285 100644 --- a/ChatSecure.xcodeproj/project.pbxproj +++ b/ChatSecure.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 63F0CAFB1E60C1B40045359C /* OTRYapViewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F0CAFA1E60C1B40045359C /* OTRYapViewTest.swift */; }; 63F614DC1BB214660083A06A /* ChatSecureModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F614DB1BB214660083A06A /* ChatSecureModelTest.swift */; }; 7CD871CB705CA365E0755104 /* libPods-ChatSecureCorePods-ChatSecureTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5179DA87B83F57EEA9589733 /* libPods-ChatSecureCorePods-ChatSecureTests.a */; }; + 8F56C60D22313225DC3E3E4E /* OTRStorageUsageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F56C0C0D2783FF60F293884 /* OTRStorageUsageViewController.swift */; }; + 8F56CEB16F4C0412C383BCF8 /* OTRStorageUsageSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F56CDC5F0CD5BA670188689 /* OTRStorageUsageSetting.swift */; }; D9108AA023F9ABDF00B1280D /* AESGCMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9108A9F23F9ABDF00B1280D /* AESGCMTests.swift */; }; D91F9EFE1ED645F100AEA62C /* FileTransferIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D91F9EFD1ED645F100AEA62C /* FileTransferIntegrationTests.swift */; }; D9365E7A1A1EB0050006434A /* torrc in Resources */ = {isa = PBXBuildFile; fileRef = D9365E791A1EB0050006434A /* torrc */; }; @@ -635,7 +637,9 @@ 6C1E59A7F629602AA386C2B8 /* Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig"; path = "Target Support Files/Pods-ChatSecureCorePods-ChatSecure/Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig"; sourceTree = ""; }; 83C35A70D105953D80691D31 /* libPods-ChatSecureCorePods-ChatSecureCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChatSecureCorePods-ChatSecureCore.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8B0F7D8477AAAE9D06628430 /* Pods-ChatSecureCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatSecureCore.release.xcconfig"; path = "Target Support Files/Pods-ChatSecureCore/Pods-ChatSecureCore.release.xcconfig"; sourceTree = ""; }; + 8F56C0C0D2783FF60F293884 /* OTRStorageUsageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTRStorageUsageViewController.swift; sourceTree = ""; }; 8F56C50436DA64774EBB16E3 /* OTRMessagesLoadingView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OTRMessagesLoadingView.xib; sourceTree = ""; }; + 8F56CDC5F0CD5BA670188689 /* OTRStorageUsageSetting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTRStorageUsageSetting.swift; sourceTree = ""; }; 9093D0A3DB37442CFB9718F8 /* Pods-ChatSecureCorePods-ChatSecureTests.macos_release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatSecureCorePods-ChatSecureTests.macos_release.xcconfig"; path = "Target Support Files/Pods-ChatSecureCorePods-ChatSecureTests/Pods-ChatSecureCorePods-ChatSecureTests.macos_release.xcconfig"; sourceTree = ""; }; 9224A5F2207E3BD800A044BF /* JoinRoomView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = JoinRoomView.xib; path = Interface/JoinRoomView.xib; sourceTree = ""; }; 924F67C41EA5541C00528FB6 /* MigrationInfoHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MigrationInfoHeaderView.xib; path = Interface/MigrationInfoHeaderView.xib; sourceTree = ""; }; @@ -1431,6 +1435,7 @@ D9C52842235CB580002B213A /* OTRCertificateDomainViewController.h */, D9C52843235CB580002B213A /* OTRComposeViewController.m */, D9C52844235CB580002B213A /* OTRLogListViewController.swift */, + 8F56C0C0D2783FF60F293884 /* OTRStorageUsageViewController.swift */, ); path = "View Controllers"; sourceTree = ""; @@ -1538,6 +1543,7 @@ D9C5286C235CB580002B213A /* OTRCertificateSetting.h */, D9C5286D235CB580002B213A /* OTRListSettingValue.m */, D9C5286E235CB580002B213A /* OTRFeedbackSetting.m */, + 8F56CDC5F0CD5BA670188689 /* OTRStorageUsageSetting.swift */, ); path = Settings; sourceTree = ""; @@ -2854,6 +2860,8 @@ D9C52A17235CB580002B213A /* PushMessage.swift in Sources */, D9C529D5235CB580002B213A /* OTRSettingsViewController.m in Sources */, D9C52A1F235CB581002B213A /* BuddySubscriptions.swift in Sources */, + 8F56C60D22313225DC3E3E4E /* OTRStorageUsageViewController.swift in Sources */, + 8F56CEB16F4C0412C383BCF8 /* OTRStorageUsageSetting.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ChatSecureCore/Classes/Controllers/OTRMediaFileManager.m b/ChatSecureCore/Classes/Controllers/OTRMediaFileManager.m index b71777804..54eebfeee 100644 --- a/ChatSecureCore/Classes/Controllers/OTRMediaFileManager.m +++ b/ChatSecureCore/Classes/Controllers/OTRMediaFileManager.m @@ -134,7 +134,7 @@ - (void)setData:(NSData *)data //#865 - (void)deleteDataForItem:(OTRMediaItem *)mediaItem buddyUniqueId:(NSString *)buddyUniqueId - completion:(void (^)(BOOL success, NSError * _Nullable error))completion + completion:(nullable void (^)(BOOL success, NSError * _Nullable error))completion completionQueue:(nullable dispatch_queue_t)completionQueue { if (!completionQueue) { completionQueue = dispatch_get_main_queue(); @@ -284,4 +284,12 @@ - (void) performAsyncWrite:(dispatch_block_t)block { } } +- (void)vacuum:(dispatch_block_t)completion { + [self performAsyncWrite:^{ + [self.ioCipher vacuum]; + if (completion != nil) { + dispatch_async(dispatch_get_main_queue(), completion); + } + }]; +} @end diff --git a/ChatSecureCore/Classes/Controllers/OTRSettingsManager.m b/ChatSecureCore/Classes/Controllers/OTRSettingsManager.m index 603be3676..89205a5bd 100644 --- a/ChatSecureCore/Classes/Controllers/OTRSettingsManager.m +++ b/ChatSecureCore/Classes/Controllers/OTRSettingsManager.m @@ -108,8 +108,11 @@ - (void) populateSettings [settingsGroups addObject:pushGroup]; } - - NSArray *chatSettings = @[deletedDisconnectedConversations]; + OTRStorageUsageSetting *storageUsageSetting = [[OTRStorageUsageSetting alloc] initWithTitle:STORAGE_USAGE_TITLE() + description:STORAGE_USAGE_DESCRIPTION()]; + storageUsageSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + + NSArray *chatSettings = @[deletedDisconnectedConversations, storageUsageSetting]; OTRSettingsGroup *chatSettingsGroup = [[OTRSettingsGroup alloc] initWithTitle:CHAT_STRING() settings:chatSettings]; [settingsGroups addObject:chatSettingsGroup]; diff --git a/ChatSecureCore/Classes/Model/Settings/OTRStorageUsageSetting.swift b/ChatSecureCore/Classes/Model/Settings/OTRStorageUsageSetting.swift new file mode 100644 index 000000000..e978591b8 --- /dev/null +++ b/ChatSecureCore/Classes/Model/Settings/OTRStorageUsageSetting.swift @@ -0,0 +1,12 @@ +// +// Created by Vyacheslav Karpukhin on 20.02.20. +// Copyright (c) 2020 Chris Ballinger. All rights reserved. +// + +import Foundation + +open class OTRStorageUsageSetting : OTRViewSetting { + public override init!(title newTitle: String!, description newDescription: String!) { + super.init(title: newTitle, description: newDescription, viewControllerClass: OTRStorageUsageViewController.self) + } +} diff --git a/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift b/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift new file mode 100644 index 000000000..e83c65a12 --- /dev/null +++ b/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift @@ -0,0 +1,195 @@ +// +// Created by Vyacheslav Karpukhin on 19.02.20. +// Copyright (c) 2020 Chris Ballinger. All rights reserved. +// + +import Foundation +import OTRAssets +import MBProgressHUD + +class OTRStorageUsageViewController : XLFormViewController { + private let ROOT_SECTION_TAG = "rootSection" + private let DELETE_ALL_TAG = "deleteAll" + private let NO_MEDIA_FOUND_TAG = "noMediaFound" + + private let connections = OTRDatabaseManager.shared.connections + + init() { + super.init(nibName: nil, bundle: nil) + self.form = self.formDescriptor() + } + + required public init!(coder aDecoder: NSCoder!) { + fatalError("init(coder:) has not been implemented") + } + + func formDescriptor() -> XLFormDescriptor { + let form = XLFormDescriptor(title: STORAGE_USAGE_TITLE()) + + let firstSection = XLFormSectionDescriptor() + firstSection.multivaluedTag = ROOT_SECTION_TAG + form.addFormSection(firstSection) + + let deleteAll = XLFormRowDescriptor(tag: DELETE_ALL_TAG, rowType: XLFormRowDescriptorTypeButton, title: STORAGE_USAGE_DELETE_ALL_BUTTON()) + deleteAll.action.formBlock = { row in + self.deselectFormRow(row) + self.deleteMedia() + } + firstSection.addFormRow(deleteAll) + + let noMediaFound = XLFormRowDescriptor(tag: NO_MEDIA_FOUND_TAG, rowType: XLFormRowDescriptorTypeText) + noMediaFound.value = STORAGE_USAGE_NO_MEDIA_FOUND() + noMediaFound.disabled = true + firstSection.addFormRow(noMediaFound) + + return form + } + + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.isEditing = false + DispatchQueue.global().async { + self.processAllMedia() + } + } + + private func processAllMedia() { + var empty = true + connections?.read.read { (transaction: YapDatabaseReadTransaction) in + transaction.enumerateKeysAndObjects(inCollection: OTRMediaItem.collection, using: { (key, object, stop) in + if let mediaItem = object as? OTRMediaItem { + let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage + + if let threadOwner = parentMessage?.threadOwner(with: transaction), + let account = threadOwner.account(with: transaction) { + do { + let length = try OTRMediaFileManager.shared.dataLength(for: mediaItem, buddyUniqueId: threadOwner.threadIdentifier) + empty = false + + let section = sectionForAccount(account) + let row = rowForThreadOwner(threadOwner, section) + let value = row.value as? Int ?? 0 + row.value = value + length.intValue + + DispatchQueue.main.async { + self.updateFormRow(row) + } + } catch { + return + } + } + } + }) + } + if let deleteAll = self.form.formRow(withTag: DELETE_ALL_TAG), + let noMediaFound = self.form.formRow(withTag: NO_MEDIA_FOUND_TAG){ + DispatchQueue.main.async { + deleteAll.hidden = empty + noMediaFound.hidden = !empty + } + } + } + + private func rowForThreadOwner(_ threadOwner: OTRThreadOwner, _ section: XLFormSectionDescriptor) -> XLFormRowDescriptor { + var row = self.form.formRow(withTag: threadOwner.threadIdentifier) + if row == nil { + row = XLFormRowDescriptor(tag: threadOwner.threadIdentifier, rowType: XLFormRowDescriptorTypeInfo, title: threadOwner.threadName) + row?.valueFormatter = ByteCountFormatter() + DispatchQueue.main.sync { + section.addFormRow(row!) + } + } + return row! + } + + private func sectionForAccount(_ account: OTRAccount) -> XLFormSectionDescriptor { + var section = (self.form.formSections as! [XLFormSectionDescriptor]).first { $0.title == account.displayName } + if section == nil { + section = XLFormSectionDescriptor.formSection(withTitle: account.displayName, sectionOptions: .canDelete) + DispatchQueue.main.sync { + self.form.addFormSection(section!) + } + } + return section! + } + + override func formRowHasBeenRemoved(_ formRow: XLFormRowDescriptor!, at indexPath: IndexPath!) { + super.formRowHasBeenRemoved(formRow, at: indexPath) + deleteMedia(formRow.tag) + } + + private func deleteMedia(_ threadIdentifier: String? = nil) { + DispatchQueue.main.async { [weak self] in + guard let strongSelf = self else { return } + MBProgressHUD.showAdded(to: strongSelf.view, animated: true) + } + connections?.write.asyncReadWrite { [weak self] (transaction: YapDatabaseReadWriteTransaction) in + guard let strongSelf = self else { return } + let mediaItemsToDelete = strongSelf.findItemsToDelete(transaction, threadIdentifier) + strongSelf.doDelete(transaction, mediaItemsToDelete) + DispatchQueue.global().async { + strongSelf.vacuumAndUpdateUI(deleteAll: threadIdentifier == nil) + } + } + } + + private func findItemsToDelete(_ transaction: YapDatabaseReadWriteTransaction, _ threadIdentifier: String?) -> [OTRMediaItem] { + var mediaItemsToDelete: [OTRMediaItem] = [] + transaction.enumerateKeysAndObjects(inCollection: OTRMediaItem.collection, using: { (key, object, stop) in + if let mediaItem = object as? OTRMediaItem { + let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage + if threadIdentifier != nil, + let threadOwner = parentMessage?.threadOwner(with: transaction), + threadOwner.threadIdentifier != threadIdentifier { + return + } + if (parentMessage?.mediaItemUniqueId != nil) { + mediaItemsToDelete.append(mediaItem) + } + } + }) + return mediaItemsToDelete + } + + private func doDelete(_ transaction: YapDatabaseReadWriteTransaction, _ mediaItemsToDelete: [OTRMediaItem]) { + mediaItemsToDelete.forEach { mediaItem in + if let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage, + let threadOwner = parentMessage.threadOwner(with: transaction) { + + mediaItem.remove(with: transaction) + + let media = OTRMediaItem.incomingItem(withFilename: mediaItem.filename, mimeType: nil) + media.parentObjectKey = parentMessage.uniqueId + media.parentObjectCollection = parentMessage.messageCollection + media.save(with: transaction) + + parentMessage.mediaItemUniqueId = media.uniqueId + parentMessage.messageError = FileTransferError.automaticDownloadsDisabled + parentMessage.save(with: transaction) + + OTRMediaFileManager.shared.deleteData(for: mediaItem, + buddyUniqueId: threadOwner.threadIdentifier, completion: nil, completionQueue: nil) + } + } + } + + private func vacuumAndUpdateUI(deleteAll: Bool) { + OTRMediaFileManager.shared.vacuum { [weak self] in + guard let strongSelf = self else { return } + if deleteAll { + strongSelf.form.formSections.forEach { + let element = $0 as! XLFormSectionDescriptor + if element.multivaluedTag != strongSelf.ROOT_SECTION_TAG { + strongSelf.form.removeFormSection(element) + } + } + if let deleteAll = strongSelf.form.formRow(withTag: strongSelf.DELETE_ALL_TAG), + let noMediaFound = strongSelf.form.formRow(withTag: strongSelf.NO_MEDIA_FOUND_TAG){ + deleteAll.hidden = true + noMediaFound.hidden = false + } + } + MBProgressHUD.hide(for: strongSelf.view, animated: true) + } + } +} diff --git a/ChatSecureCore/Public/OTRMediaFileManager.h b/ChatSecureCore/Public/OTRMediaFileManager.h index 9053211f7..0a2aef45c 100644 --- a/ChatSecureCore/Public/OTRMediaFileManager.h +++ b/ChatSecureCore/Public/OTRMediaFileManager.h @@ -33,7 +33,7 @@ completionQueue:(nullable dispatch_queue_t)completionQueue; //#865 - (void)deleteDataForItem:(OTRMediaItem *)mediaItem buddyUniqueId:(NSString *)buddyUniqueId - completion:(void (^)(BOOL success, NSError * _Nullable error))completion + completion:(nullable void (^)(BOOL success, NSError * _Nullable error))completion completionQueue:(nullable dispatch_queue_t)completionQueue; - (nullable NSData*)dataForItem:(OTRMediaItem *)mediaItem @@ -46,6 +46,8 @@ completionQueue:(nullable dispatch_queue_t)completionQueue; + (nullable NSString *)pathForMediaItem:(OTRMediaItem *)mediaItem buddyUniqueId:(NSString *)buddyUniqueId; + (nullable NSString *)pathForMediaItem:(OTRMediaItem *)mediaItem buddyUniqueId:(NSString *)buddyUniqueId withLeadingSlash:(BOOL)includeLeadingSlash; +- (void)vacuum:(dispatch_block_t)completion; + @property (class, nonatomic, readonly) OTRMediaFileManager *shared; + (instancetype)sharedInstance; diff --git a/OTRAssets/Strings/OTRStrings.h b/OTRAssets/Strings/OTRStrings.h index b78b1ced8..81f04a5ba 100644 --- a/OTRAssets/Strings/OTRStrings.h +++ b/OTRAssets/Strings/OTRStrings.h @@ -476,6 +476,14 @@ FOUNDATION_EXPORT NSString* SOMEONE_IS_TYPING_STRING(); FOUNDATION_EXPORT NSString* SOMEONE_STRING(); /** "Check out the source here on Github", let users know source is on Github */ FOUNDATION_EXPORT NSString* SOURCE_STRING(); +/** "Delete All", Delete all media button */ +FOUNDATION_EXPORT NSString* STORAGE_USAGE_DELETE_ALL_BUTTON(); +/** "Manage downloaded media", Storage usage setting description */ +FOUNDATION_EXPORT NSString* STORAGE_USAGE_DESCRIPTION(); +/** "No downloaded media found in chats", No media found clarification */ +FOUNDATION_EXPORT NSString* STORAGE_USAGE_NO_MEDIA_FOUND(); +/** "Storage Usage", Storage usage setting title */ +FOUNDATION_EXPORT NSString* STORAGE_USAGE_TITLE(); /** "Server", server selection section title */ FOUNDATION_EXPORT NSString* Server_String(); /** "Choose from a selection of public servers, or use your own.", server selection footer */ diff --git a/OTRAssets/Strings/OTRStrings.m b/OTRAssets/Strings/OTRStrings.m index 353cca845..72a259979 100644 --- a/OTRAssets/Strings/OTRStrings.m +++ b/OTRAssets/Strings/OTRStrings.m @@ -476,6 +476,14 @@ NSString* SOMEONE_STRING() { return [OTRLanguageManager translatedString:@"Someone"]; } /** "Check out the source here on Github", let users know source is on Github */ NSString* SOURCE_STRING() { return [OTRLanguageManager translatedString:@"Check out the source here on Github"]; } +/** "Delete All", Delete all media button */ +NSString* STORAGE_USAGE_DELETE_ALL_BUTTON() { return [OTRLanguageManager translatedString:@"Delete All"]; } +/** "Manage downloaded media", Storage usage setting description */ +NSString* STORAGE_USAGE_DESCRIPTION() { return [OTRLanguageManager translatedString:@"Manage downloaded media"]; } +/** "No downloaded media found in chats", No media found clarification */ +NSString* STORAGE_USAGE_NO_MEDIA_FOUND() { return [OTRLanguageManager translatedString:@"No downloaded media found in chats"]; } +/** "Storage Usage", Storage usage setting title */ +NSString* STORAGE_USAGE_TITLE() { return [OTRLanguageManager translatedString:@"Storage Usage"]; } /** "Server", server selection section title */ NSString* Server_String() { return [OTRLanguageManager translatedString:@"Server"]; } /** "Choose from a selection of public servers, or use your own.", server selection footer */ diff --git a/OTRAssets/Strings/strings.json b/OTRAssets/Strings/strings.json index 4b475ca8d..e82cbf42d 100644 --- a/OTRAssets/Strings/strings.json +++ b/OTRAssets/Strings/strings.json @@ -1245,5 +1245,17 @@ }, "ADD_FRIEND_TO_AUTO_DOWNLOAD": { "comment": "Shown in chat view to prompt user to add friend for auto-download of group media messages.", "string": "Is %@ your friend? Add him/her to auto-download pictures in the future." + }, "STORAGE_USAGE_TITLE": { + "comment": "Storage usage setting title", + "string": "Storage Usage" + }, "STORAGE_USAGE_DESCRIPTION": { + "comment": "Storage usage setting description", + "string": "Manage downloaded media" + }, "STORAGE_USAGE_DELETE_ALL_BUTTON": { + "comment": "Delete all media button", + "string": "Delete All" + }, "STORAGE_USAGE_NO_MEDIA_FOUND": { + "comment": "No media found clarification", + "string": "No downloaded media found in chats" } } diff --git a/OTRResources/Localizations/Base.lproj/Localizable.strings b/OTRResources/Localizations/Base.lproj/Localizable.strings index 9fc5b857bf220a5c3492d16ff84e04e8eb341809..a0134fdcee73a1f618a377f8192076db4429e447 100644 GIT binary patch delta 476 zcmdlmljYhxmJJzhsyPg)3?)FUz>vt01B40;xjvgH3Y0CG%;+Z143wSx z-%XO$5u|Ri{wMX#HEv=ClM`%NMDrPnfXdUs#*{J?L)eoi3JOnduvD1*ri@QMg(07z zoFNaWH6JLO0=5Fls>!QMB?+qe=%PGXtCVl^ma+}JlMC3{ z5*dmnH|`Uj{Hl$wUI8eI*Lt8HWc`^yb3mqK08J?YdPfP!vjt)$20x%VB%8*AY~19J z*}{_-m}+gF&~n3xK;T{2E5=^}v=I@wp_4zp5Ecvux(Zcv;!M@a3(D9w*Bm!-1pt4? Bbp8MU delta 39 ucmcaMk7dJ5mJJzhn;*Do8BB5&*?g~jAMfM@EuGD)+HN?31e8vhxB>uYBoST! diff --git a/Submodules/IOCipher b/Submodules/IOCipher index 7a68be1db..89aee7f8c 160000 --- a/Submodules/IOCipher +++ b/Submodules/IOCipher @@ -1 +1 @@ -Subproject commit 7a68be1dbed27fc39b8f5888d95d8a2e0cb4f16d +Subproject commit 89aee7f8c7f0e23a484d17e70cd8ea2836d22edd diff --git a/Submodules/libsqlfs b/Submodules/libsqlfs index 4456f8a83..973f8a61a 160000 --- a/Submodules/libsqlfs +++ b/Submodules/libsqlfs @@ -1 +1 @@ -Subproject commit 4456f8a83c7af7dc6b8cf6456dae94fe7979c24c +Subproject commit 973f8a61a5486064f33ebd2bd7fa59c142d3bf02 From 5efdc773fb4b5ebc09b5156bf1f08aea4389bb49 Mon Sep 17 00:00:00 2001 From: Chris Ballinger Date: Thu, 26 Mar 2020 19:51:13 -0700 Subject: [PATCH 2/2] Refactor for YapDatabase 4.0 --- .../OTRStorageUsageViewController.swift | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift b/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift index e83c65a12..588b4976d 100644 --- a/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift +++ b/ChatSecureCore/Classes/View Controllers/OTRStorageUsageViewController.swift @@ -56,30 +56,24 @@ class OTRStorageUsageViewController : XLFormViewController { private func processAllMedia() { var empty = true connections?.read.read { (transaction: YapDatabaseReadTransaction) in - transaction.enumerateKeysAndObjects(inCollection: OTRMediaItem.collection, using: { (key, object, stop) in - if let mediaItem = object as? OTRMediaItem { - let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage - - if let threadOwner = parentMessage?.threadOwner(with: transaction), - let account = threadOwner.account(with: transaction) { - do { - let length = try OTRMediaFileManager.shared.dataLength(for: mediaItem, buddyUniqueId: threadOwner.threadIdentifier) - empty = false - - let section = sectionForAccount(account) - let row = rowForThreadOwner(threadOwner, section) - let value = row.value as? Int ?? 0 - row.value = value + length.intValue - - DispatchQueue.main.async { - self.updateFormRow(row) - } - } catch { - return - } - } + transaction.iterateKeysAndObjects(inCollection: OTRMediaItem.collection) { (key, mediaItem: OTRMediaItem, stop) in + let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage + guard let threadOwner = parentMessage?.threadOwner(with: transaction), + let account = threadOwner.account(with: transaction), + let length = try? OTRMediaFileManager.shared.dataLength(for: mediaItem, buddyUniqueId: threadOwner.threadIdentifier) else { + return + } + empty = false + + let section = sectionForAccount(account) + let row = rowForThreadOwner(threadOwner, section) + let value = row.value as? Int ?? 0 + row.value = value + length.intValue + + DispatchQueue.main.async { + self.updateFormRow(row) } - }) + } } if let deleteAll = self.form.formRow(withTag: DELETE_ALL_TAG), let noMediaFound = self.form.formRow(withTag: NO_MEDIA_FOUND_TAG){ @@ -135,19 +129,17 @@ class OTRStorageUsageViewController : XLFormViewController { private func findItemsToDelete(_ transaction: YapDatabaseReadWriteTransaction, _ threadIdentifier: String?) -> [OTRMediaItem] { var mediaItemsToDelete: [OTRMediaItem] = [] - transaction.enumerateKeysAndObjects(inCollection: OTRMediaItem.collection, using: { (key, object, stop) in - if let mediaItem = object as? OTRMediaItem { - let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage - if threadIdentifier != nil, - let threadOwner = parentMessage?.threadOwner(with: transaction), - threadOwner.threadIdentifier != threadIdentifier { - return - } - if (parentMessage?.mediaItemUniqueId != nil) { - mediaItemsToDelete.append(mediaItem) - } + transaction.iterateKeysAndObjects(inCollection: OTRMediaItem.collection) { (key, mediaItem: OTRMediaItem, stop) in + let parentMessage = mediaItem.parentMessage(with: transaction) as? OTRBaseMessage + if threadIdentifier != nil, + let threadOwner = parentMessage?.threadOwner(with: transaction), + threadOwner.threadIdentifier != threadIdentifier { + return } - }) + if (parentMessage?.mediaItemUniqueId != nil) { + mediaItemsToDelete.append(mediaItem) + } + } return mediaItemsToDelete }