From b4aa3d732f6cd63e5b8fbebfb0c609970fb81d14 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 03:06:47 +0100 Subject: [PATCH 01/12] key backup: Add a dedicated section to settings #2193 Still WIP --- Riot.xcodeproj/project.pbxproj | 32 ++ Riot/Assets/en.lproj/Vector.strings | 16 + Riot/Categories/UIControl.swift | 38 +++ Riot/Generated/Strings.swift | 60 ++++ .../SettingsKeyBackupTableViewSection.swift | 312 ++++++++++++++++++ .../SettingsKeyBackupViewAction.swift | 25 ++ .../SettingsKeyBackupViewModel.swift | 142 ++++++++ .../SettingsKeyBackupViewModelType.swift | 32 ++ .../SettingsKeyBackupViewState.swift | 33 ++ .../Modules/Settings/SettingsViewController.m | 202 +++++++++++- 10 files changed, 886 insertions(+), 6 deletions(-) create mode 100644 Riot/Categories/UIControl.swift create mode 100644 Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift create mode 100644 Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift create mode 100644 Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift create mode 100644 Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift create mode 100644 Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 940d86d1a..8b1f1605c 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -25,8 +25,14 @@ 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; + 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; }; 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; }; 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; }; + 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */; }; + 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */; }; + 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; }; + 32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */; }; + 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */; }; 358DB9429359F97520545D35 /* Pods_RiotPods_RiotShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5856BA7A55E53C0AEAFC084 /* Pods_RiotPods_RiotShareExtension.framework */; }; 89C94E649229EA68AE787E9E /* Pods_RiotPods_Riot.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2510A69B4A681C1FEC36E848 /* Pods_RiotPods_Riot.framework */; }; 926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */; }; @@ -469,6 +475,7 @@ 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = ""; }; 3267EFB620E379FD00FF1CAA /* README.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.rst; sourceTree = ""; }; 3275FD8B21A5A2C500B9C13D /* TermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = ""; }; + 3281BCF62201FA4200F4A383 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; 3284A35020A07C210044F922 /* postMessageAPI.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = postMessageAPI.js; sourceTree = ""; }; 32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = ""; }; 32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -477,6 +484,11 @@ 32BDC9A4211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/InfoPlist.strings; sourceTree = ""; }; 32BDC9A5211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = ""; }; 32BDC9A6211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Vector.strings; sourceTree = ""; }; + 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModel.swift; sourceTree = ""; }; + 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModelType.swift; sourceTree = ""; }; + 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewState.swift; sourceTree = ""; }; + 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewAction.swift; sourceTree = ""; }; + 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupTableViewSection.swift; sourceTree = ""; }; 32D7159E2146CC6F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Vector.strings; sourceTree = ""; }; 32D7159F2146CC7F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 32D715A02146CC8800DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -1160,6 +1172,18 @@ path = js; sourceTree = ""; }; + 32BF994D21FA1C6300698084 /* KeyBackup */ = { + isa = PBXGroup; + children = ( + 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */, + 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */, + 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */, + 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */, + 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */, + ); + path = KeyBackup; + sourceTree = ""; + }; 5FC42FA41F5186AFFB6A2404 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1599,6 +1623,7 @@ B1B5567F20EE6C4C00210D55 /* SettingsViewController.h */, B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */, B1B5578120EF564900210D55 /* Views */, + 32BF994D21FA1C6300698084 /* KeyBackup */, B1B5567B20EE6C4C00210D55 /* Language */, B1B5567820EE6C4C00210D55 /* PhoneCountry */, B1B5568020EE6C4C00210D55 /* DeactivateAccount */, @@ -2801,6 +2826,7 @@ B1CA3A2821EF692B000D1D89 /* UIView.swift */, B140B4A121F87F7100E3F5FE /* OperationQueue.swift */, B1E5368821FB1E20001F3AFF /* UIButton.swift */, + 3281BCF62201FA4200F4A383 /* UIControl.swift */, ); path = Categories; sourceTree = ""; @@ -3425,6 +3451,7 @@ B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */, B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */, + 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */, B1B558E120EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.m in Sources */, B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */, B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */, @@ -3574,8 +3601,11 @@ B1B558D320EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.m in Sources */, B1B5576F20EE702800210D55 /* IntegrationManagerViewController.m in Sources */, B1B557AC20EF5A6D00210D55 /* DeviceView.m in Sources */, + 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */, B16932EE20F3C3C900746532 /* FilesSearchCellData.m in Sources */, B1B558E520EF768F00210D55 /* RoomMembershipExpandedBubbleCell.m in Sources */, + 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */, + 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */, B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, B139C22D21FF32F000BB68EC /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */, @@ -3583,6 +3613,7 @@ F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, B1B5572C20EE6C4D00210D55 /* RoomParticipantsViewController.m in Sources */, B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, + 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */, @@ -3594,6 +3625,7 @@ B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */, B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */, B1B5577D20EE84BF00210D55 /* CircleButton.m in Sources */, + 32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */, B1B558FF20EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.m in Sources */, B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */, B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */, diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a1b7a6015..9ad236d78 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -335,6 +335,7 @@ "settings_flair" = "Show flair where allowed"; "settings_devices" = "DEVICES"; "settings_cryptography" = "CRYPTOGRAPHY"; +"settings_key_backup" = "SECURE MESSAGE"; "settings_deactivate_account" = "DEACTIVATE ACCOUNT"; "settings_sign_out" = "Sign Out"; @@ -420,6 +421,21 @@ "settings_deactivate_my_account" = "Deactivate my account"; +"settings_key_backup_info_none" = "No backup is present"; +"settings_key_backup_info_version" = "Backup version: %@"; +"settings_key_backup_info_algorithm" = "Algorithm: %@"; +"settings_key_backup_info_valid" = "This device is using key backup"; +"settings_key_backup_info_not_valid" = "This device is not using key backup"; +"settings_key_backup_info_progress" = "Backing up %@ keys..."; +"settings_key_backup_info_progress_done" = "All keys backed up"; +"settings_key_backup_info_trust_signature_unknown" = "Backup has a signature from unknown device with ID %@."; +"settings_key_backup_info_trust_signature_valid" = "Backup has a valid signature from this device"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Backup has a valid signature from verified device %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Backup has a valid signature from unverified device %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has a invalid signature from verified device %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has a invalid signature from unverified device %@"; +"settings_key_backup_button_create" = "Start a new backup"; + // Room Details "room_details_title" = "Room Details"; "room_details_people" = "Members"; diff --git a/Riot/Categories/UIControl.swift b/Riot/Categories/UIControl.swift new file mode 100644 index 000000000..a6323581a --- /dev/null +++ b/Riot/Categories/UIControl.swift @@ -0,0 +1,38 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +// Source: https://stackoverflow.com/a/44917661 +class ClosureSleeve { + let closure: () -> () + + init(attachTo: AnyObject, closure: @escaping () -> ()) { + self.closure = closure + objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN) + } + + @objc func invoke() { + closure() + } +} + +extension UIControl { + func vc_addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: @escaping () -> ()) { + let sleeve = ClosureSleeve(attachTo: self, closure: action) + addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents) + } +} diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 0ab2019e5..ecaaf2e4f 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1918,6 +1918,66 @@ internal enum VectorL10n { internal static var settingsIgnoredUsers: String { return VectorL10n.tr("Vector", "settings_ignored_users") } + /// SECURE MESSAGE + internal static var settingsKeyBackup: String { + return VectorL10n.tr("Vector", "settings_key_backup") + } + /// Start a new backup + internal static var settingsKeyBackupButtonCreate: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_create") + } + /// Algorithm: %@ + internal static func settingsKeyBackupInfoAlgorithm(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_algorithm", p1) + } + /// No backup is present + internal static var settingsKeyBackupInfoNone: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_none") + } + /// This device is not using key backup + internal static var settingsKeyBackupInfoNotValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_not_valid") + } + /// Backing up %@ keys... + internal static func settingsKeyBackupInfoProgress(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_progress", p1) + } + /// All keys backed up + internal static var settingsKeyBackupInfoProgressDone: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done") + } + /// Backup has a invalid signature from unverified device %@ + internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_unverified", p1) + } + /// Backup has a invalid signature from verified device %@ + internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_verified", p1) + } + /// Backup has a signature from unknown device with ID %@. + internal static func settingsKeyBackupInfoTrustSignatureUnknown(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_unknown", p1) + } + /// Backup has a valid signature from this device + internal static var settingsKeyBackupInfoTrustSignatureValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid") + } + /// Backup has a valid signature from unverified device %@ + internal static func settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_unverified", p1) + } + /// Backup has a valid signature from verified device %@ + internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1) + } + /// This device is using key backup + internal static var settingsKeyBackupInfoValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_valid") + } + /// Backup version: %@ + internal static func settingsKeyBackupInfoVersion(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_version", p1) + } /// LABS internal static var settingsLabs: String { return VectorL10n.tr("Vector", "settings_labs") diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift new file mode 100644 index 000000000..e1e646d07 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -0,0 +1,312 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +@objc protocol SettingsKeyBackupTableViewSectionDelegate: class { + func settingsKeyBackupTableViewSectionDidUpdate(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) + + func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView + func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton + + + func settingsKeyBackupTableViewSectionShowKeyBackupSetup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showVerifyDevice deviceId:String) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) +} + +@objc class SettingsKeyBackupTableViewSection: NSObject { + + // MARK: - Properties + + @objc weak var delegate: SettingsKeyBackupTableViewSectionDelegate? + @objc var tableViewCells: [UITableViewCell] + + // MARK: Private + + // This view class holds the model because the model is in pure Swift + // whereas this class can be used from objC + private var viewModel: SettingsKeyBackupViewModelType! + + // MARK: - Public + + @objc init(withKeyBackup keyBackup: MXKeyBackup) { + self.tableViewCells = [] + self.viewModel = SettingsKeyBackupViewModel(keyBackup: keyBackup) + super.init() + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .load) + } + + @objc func reload() { + self.viewModel.process(viewAction: .load) + } + + // MARK: - Private + + private func render(viewState: SettingsKeyBackupViewState) { + + guard let delegate = self.delegate else { + return + } + + switch viewState { + case .checkingBackup: + self.renderLoading() + + case .noBackup: + self.renderNoKeyBackup() + + case .backup(let keyBackupVersion, let keyBackupVersionTrust): + self.renderKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust) + + case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): + self.renderRunningKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust, backupProgress: backupProgress) + + case .backupButNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + self.renderNotTrustedKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust) + } + + delegate.settingsKeyBackupTableViewSectionDidUpdate(self) + } + + private func renderLoading() { + // TODO: loading wheel + self.tableViewCells = [] + } + + private func renderNoKeyBackup() { + guard let delegate = self.delegate else { + return + } + + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + infoCell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoNone + + self.tableViewCells = [infoCell] + + // Add buttons + self.tableViewCells = [infoCell] + self.buttonCellForCreate(fromCellIndex: self.tableViewCells.count) + } + + private func renderKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust) { + + guard let delegate = self.delegate, + let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + + let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + // Add buttons + self.tableViewCells = [infoCell] + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: self.tableViewCells.count) + } + + private func renderRunningKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust, backupProgress:Progress) { + + guard let delegate = self.delegate, + let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount + + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining)) + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + + let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + // Add buttons + self.tableViewCells = [infoCell] + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: 1) + } + + + private func renderNotTrustedKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust) { + + guard let delegate = self.delegate, + let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoNotValid + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + + let strings = [version, algorithm, backupStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + self.tableViewCells = [infoCell] + + // Display a verify button for the last non verified device only + let deviceId = self.lastNonVerifiedDevice(keyBackupVersionTrust) + self.tableViewCells = self.tableViewCells + self.buttonCellForVerifyingDevice(deviceId, fromCellIndex: self.tableViewCells.count) + + // Add buttons + self.tableViewCells = self.tableViewCells + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: self.tableViewCells.count) + } + + + // MARK: - Data Computing + + private func stringForKeyBackupTrust(_ keyBackupVersionTrust: MXKeyBackupVersionTrust) -> [String] { + + return keyBackupVersionTrust.signatures.map { (signature) -> String in + guard let device = signature.device else { + return VectorL10n.settingsKeyBackupInfoTrustSignatureUnknown(signature.deviceId) + } + + let displayName = device.displayName ?? device.deviceId ?? "" + + if device.fingerprint == "" { // TODO + return VectorL10n.settingsKeyBackupInfoTrustSignatureValid + } + else if signature.valid && (device.verified == MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceVerified(displayName) + } + else if signature.valid && (device.verified != MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(displayName) + } + else if !signature.valid && (device.verified == MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(displayName) + } + else if !signature.valid && (device.verified != MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(displayName) + } + + return ""; + } + } + + private func lastNonVerifiedDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> String? + { + var lastNonVerifiedDeviceId: String? + for signature in keyBackupVersionTrust.signatures.reversed() { + + guard let device = signature.device else { + continue + } + + if device.verified != MXDeviceVerified + { + lastNonVerifiedDeviceId = device.deviceId + break + } + } + return lastNonVerifiedDeviceId + } + + // MARK: - Cells + + private func buttonCellForCreate(fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + + guard let delegate = self.delegate else { + return [] + } + + let verifyCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: self.tableViewCells.count) + + let btnTitle = "Start a new backup" + verifyCell.mxkButton.setTitle(btnTitle, for: .normal) + verifyCell.mxkButton.setTitle(btnTitle, for: .highlighted) + + verifyCell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .create) + } + + return [verifyCell] + } + + private func buttonCellForVerifyingDevice(_ deviceId: String?, fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + + guard let deviceId = deviceId, let delegate = self.delegate else { + return [] + } + + let verifyCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: self.tableViewCells.count) + + let btnTitle = "Verify..." + verifyCell.mxkButton.setTitle(btnTitle, for: .normal) + verifyCell.mxkButton.setTitle(btnTitle, for: .highlighted) + + verifyCell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .verify(deviceId)) + } + + return [verifyCell] + } + + private func buttonCellsForRestoreAndDelete(_ keyBackupVersion: MXKeyBackupVersion, fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + guard let delegate = self.delegate else { + return [] + } + + let restoreCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: fromCellIndex) + let restoreTitle = "Restore backup" + restoreCell.mxkButton.setTitle(restoreTitle, for: .normal) + restoreCell.mxkButton.setTitle(restoreTitle, for: .highlighted) + restoreCell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .restore(keyBackupVersion)) + } + + let deleteCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: fromCellIndex + 1) + let deleteTitle = "Delete backup" + deleteCell.mxkButton.setTitle(deleteTitle, for: .normal) + deleteCell.mxkButton.setTitle(deleteTitle, for: .highlighted) + deleteCell.mxkButton.tintColor = ThemeService.shared().theme.warningColor + deleteCell.mxkButton.vc_addAction { + // TODO: Ask confirmation + self.viewModel.process(viewAction: .delete(keyBackupVersion)) + } + + return [restoreCell, deleteCell] + } +} + +// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate +extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDelegate { + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) { + self.render(viewState: viewSate) + } + + func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) { + self.delegate?.settingsKeyBackupTableViewSectionShowKeyBackupSetup(self) + } + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showVerifyDevice deviceId: String) { + self.delegate?.settingsKeyBackup(self, showVerifyDevice: deviceId) + } + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) + } +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift new file mode 100644 index 000000000..aab4be8e4 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift @@ -0,0 +1,25 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +enum SettingsKeyBackupViewAction { + case load + case create + case verify(String) + case restore(MXKeyBackupVersion) + case delete(MXKeyBackupVersion) +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift new file mode 100644 index 000000000..d93497fb7 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -0,0 +1,142 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { + + // MARK: - Properties + weak var viewDelegate: SettingsKeyBackupViewModelViewDelegate? + + // MARK: Private + private let keyBackup: MXKeyBackup + + init(keyBackup: MXKeyBackup) { + self.keyBackup = keyBackup + self.registerKeyBackupVersionDidChangeStateNotification() + } + + private func registerKeyBackupVersionDidChangeStateNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(keyBackupDidStateChange), name: NSNotification.Name.mxKeyBackupDidStateChange, object: self.keyBackup) + } + + @objc private func keyBackupDidStateChange() { + self.checkKeyBackupState() + } + + func process(viewAction: SettingsKeyBackupViewAction) { + guard let viewDelegate = self.viewDelegate else { + return + } + + switch viewAction { + case .load: + self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) + self.checkKeyBackupState() + case .create: + viewDelegate.settingsKeyBackupViewModelShowKeyBackupSetup(self) + break + case .verify(let deviceId): + viewDelegate.settingsKeyBackup(self, showVerifyDevice: deviceId) + break + case .restore(let keyBackupVersion): + viewDelegate.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) + break + case .delete(let keyBackupVersion): + self.deleteKeyBackupVersion(keyBackupVersion) + break + } + } + + // MARK: - Private + + func checkKeyBackupState() { + + if let keyBackupVersion = self.keyBackup.keyBackupVersion { + + self.keyBackup.trust(for: keyBackupVersion, onComplete: { [weak self] (keyBackupVersionTrust) in + + guard let sself = self else { + return + } + + sself.computeState(withBackupVersionTrust:keyBackupVersionTrust) + }) + } + else { + computeState() + } + } + + func computeState(withBackupVersionTrust keyBackupVersionTrust:MXKeyBackupVersionTrust? = nil) { + + var viewState: SettingsKeyBackupViewState? + switch self.keyBackup.state { + + case MXKeyBackupStateUnknown, + MXKeyBackupStateCheckingBackUpOnHomeserver: + viewState = .checkingBackup + + case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling: + viewState = .noBackup + + case MXKeyBackupStateNotTrusted: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backupButNotTrusted(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateReadyToBackUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backup(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + + // Get the backup progress before updating the state + self.keyBackup.backupProgress { [weak self] (progress) in + guard let sself = self else { + return + } + + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateViewState: .backupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) + } + default: + break + } + + if let vviewState = viewState { + self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: vviewState) + } + } + + func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) { + guard let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { + + }) { (Error) in + + } + + } +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift new file mode 100644 index 000000000..41b03dd77 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -0,0 +1,32 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +protocol SettingsKeyBackupViewModelViewDelegate: class { + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) + + func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showVerifyDevice deviceId:String) + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) +} + +protocol SettingsKeyBackupViewModelType { + + var viewDelegate: SettingsKeyBackupViewModelViewDelegate? { get set } + + func process(viewAction: SettingsKeyBackupViewAction) +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift new file mode 100644 index 000000000..2e1ad30d6 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift @@ -0,0 +1,33 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +/// SettingsKeyBackup view state +/// +/// - checkingBackup: Load current backup on the homeserver +/// - checkError: Fail to load current backup data +/// - noBackup: There is no backup on the homeserver +/// - backup: There is a valid backup on the homeserver. All keys have been backed up to it +/// - backupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it +/// - backupButNotVerified: There is a backup on the homeserver but it has not been verified yet +enum SettingsKeyBackupViewState { + case checkingBackup + case noBackup + case backup(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case backupButNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) +} diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 18d7e3fb7..451c16661 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -48,6 +48,8 @@ #import "Riot-Swift.h" +#import "EncryptionInfoView.h" + NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId"; enum @@ -63,8 +65,9 @@ enum SETTINGS_SECTION_OTHER_INDEX, SETTINGS_SECTION_LABS_INDEX, SETTINGS_SECTION_CRYPTOGRAPHY_INDEX, - SETTINGS_SECTION_FLAIR_INDEX, + SETTINGS_SECTION_KEYBACKUP_INDEX, SETTINGS_SECTION_DEVICES_INDEX, + SETTINGS_SECTION_FLAIR_INDEX, SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX, SETTINGS_SECTION_COUNT }; @@ -135,7 +138,11 @@ enum { typedef void (^blockSettingsViewController_onReadyToDestroy)(void); -@interface SettingsViewController () +@interface SettingsViewController () { // Current alert (if any). UIAlertController *currentAlert; @@ -227,6 +234,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // The current pushed view controller UIViewController *pushedViewController; + + SettingsKeyBackupTableViewSection *keyBackupSection; + KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; + KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter; } /** @@ -314,7 +325,13 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { [self addMatrixSession:mxSession]; } - + + if (self.mainSession.crypto.backup) + { + keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup]; + keyBackupSection.delegate = self; + } + groupsDataSource = [[GroupsDataSource alloc] initWithMatrixSession:self.mainSession]; [groupsDataSource finalizeInitialization]; groupsDataSource.delegate = self; @@ -404,6 +421,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); [super destroy]; } + + keyBackupSetupCoordinatorBridgePresenter = nil; + keyBackupRecoverCoordinatorBridgePresenter = nil; } - (void)onMatrixSessionStateDidChange:(NSNotification *)notif @@ -1279,6 +1299,14 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); count = CRYPTOGRAPHY_COUNT; } } + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) + { + // Check whether this section is visible. + if (self.mainSession.crypto) + { + count = keyBackupSection.tableViewCells.count; + } + } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) { count = 1; @@ -2205,6 +2233,13 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); cell = exportKeysBtnCell; } } + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) + { + if (row < keyBackupSection.tableViewCells.count) + { + cell = keyBackupSection.tableViewCells[row]; + } + } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) { MXKTableViewCellWithButton *deactivateAccountBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]]; @@ -2308,12 +2343,12 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil); } } - else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX) + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) { // Check whether this section is visible if (self.mainSession.crypto) { - return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil); + return NSLocalizedStringFromTable(@"settings_key_backup", @"Vector", nil); } } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) @@ -4206,7 +4241,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); - (void)deactivateAccountViewControllerDidDeactivateWithSuccess:(DeactivateAccountViewController *)deactivateAccountViewController { NSLog(@"[SettingsViewController] Deactivate account with success"); - [[AppDelegate theDelegate] logoutSendingRequestServer:NO completion:^(BOOL isLoggedOut) { NSLog(@"[SettingsViewController] Complete clear user data after account deactivation"); @@ -4218,4 +4252,160 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); [deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil]; } +#pragma mark - SettingsKeyBackupTableViewSectionDelegate + +- (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection +{ + [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:SETTINGS_SECTION_KEYBACKUP_INDEX] + withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +- (MXKTableViewCellWithTextView *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection textCellForRow:(NSInteger)textCellForRow +{ + return [self textViewCellForTableView:self.tableView atIndexPath:[NSIndexPath indexPathForRow:textCellForRow inSection:SETTINGS_SECTION_KEYBACKUP_INDEX]]; +} + +- (MXKTableViewCellWithButton *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow +{ + MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]]; + + if (!cell) + { + cell = [[MXKTableViewCellWithButton alloc] init]; + } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + cell.mxkButton.titleLabel.text = nil; + } + + cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; + [cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; + + [cell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; + cell.mxkButton.accessibilityIdentifier = nil; + + return cell; +} + +- (void)settingsKeyBackupTableViewSectionShowKeyBackupSetup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection +{ + [self showKeyBackupSetup]; +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showVerifyDevice:(NSString *)deviceId +{ + MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.myUser.userId deviceId:deviceId]; + [self showDeviceInfo:deviceInfo]; +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion +{ + [self showKeyBackupRecover:keyBackupVersion]; +} + +#pragma mark - MXKEncryptionInfoView + +- (void)showDeviceInfo:(MXDeviceInfo*)deviceInfo +{ + // Show it modally on the root view controller + // TODO: Improve it + UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController; + if (rootViewController) + { + EncryptionInfoView *encryptionInfoView = [[EncryptionInfoView alloc] initWithDeviceInfo:deviceInfo andMatrixSession:self.mainSession]; + [encryptionInfoView onButtonPressed:encryptionInfoView.verifyButton]; + + encryptionInfoView.delegate = self; + + // Add shadow on added view + encryptionInfoView.layer.cornerRadius = 5; + encryptionInfoView.layer.shadowOffset = CGSizeMake(0, 1); + encryptionInfoView.layer.shadowOpacity = 0.5f; + + // Add the view and define edge constraints + [rootViewController.view addSubview:encryptionInfoView]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:rootViewController.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0f + constant:10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:rootViewController.bottomLayoutGuide + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:-10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:rootViewController.view + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:encryptionInfoView + attribute:NSLayoutAttributeLeading + multiplier:1.0f + constant:-10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:rootViewController.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:encryptionInfoView + attribute:NSLayoutAttributeTrailing + multiplier:1.0f + constant:10.0f]]; + [rootViewController.view setNeedsUpdateConstraints]; + } +} + +- (void)encryptionInfoView:(MXKEncryptionInfoView*)encryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo*)deviceInfo +{ + [keyBackupSection reload]; +} + +#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter + +- (void)showKeyBackupSetup +{ + keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + + [keyBackupSetupCoordinatorBridgePresenter presentFrom:self animated:true]; + keyBackupSetupCoordinatorBridgePresenter.delegate = self; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; + + [keyBackupSection reload]; +} + +#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter + +- (void)showKeyBackupRecover:(MXKeyBackupVersion*)keyBackupVersion +{ + keyBackupRecoverCoordinatorBridgePresenter = [[KeyBackupRecoverCoordinatorBridgePresenter alloc] initWithSession:self.mainSession keyBackupVersion:keyBackupVersion]; + + [keyBackupRecoverCoordinatorBridgePresenter presentFrom:self animated:true]; + keyBackupRecoverCoordinatorBridgePresenter.delegate = self; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidCancel:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupRecoverCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupRecoverCoordinatorBridgePresenter = nil; +} + @end From 71f6b06f027e2adb83f7c1941c45c8dac9b37b07 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 09:45:03 +0100 Subject: [PATCH 02/12] key backup settings: add delete confirmation dialog --- Riot/Assets/en.lproj/Vector.strings | 3 ++ Riot/Generated/Strings.swift | 12 ++++++++ .../SettingsKeyBackupTableViewSection.swift | 13 ++++++-- .../SettingsKeyBackupViewAction.swift | 1 + .../SettingsKeyBackupViewModel.swift | 3 ++ .../SettingsKeyBackupViewModelType.swift | 1 + .../Modules/Settings/SettingsViewController.m | 30 +++++++++++++++++++ 7 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9ad236d78..f1b8659bf 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -435,6 +435,9 @@ "settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has a invalid signature from verified device %@"; "settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has a invalid signature from unverified device %@"; "settings_key_backup_button_create" = "Start a new backup"; +"settings_key_backup_button_delete" = "Delete backup"; +"settings_key_backup_delete_confirmation_prompt_title" = "Delete Backup"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history"; // Room Details "room_details_title" = "Room Details"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index ecaaf2e4f..7f8c68f57 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1926,6 +1926,18 @@ internal enum VectorL10n { internal static var settingsKeyBackupButtonCreate: String { return VectorL10n.tr("Vector", "settings_key_backup_button_create") } + /// Delete backup + internal static var settingsKeyBackupButtonDelete: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_delete") + } + /// Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history + internal static var settingsKeyBackupDeleteConfirmationPromptMsg: String { + return VectorL10n.tr("Vector", "settings_key_backup_delete_confirmation_prompt_msg") + } + /// Delete Backup + internal static var settingsKeyBackupDeleteConfirmationPromptTitle: String { + return VectorL10n.tr("Vector", "settings_key_backup_delete_confirmation_prompt_title") + } /// Algorithm: %@ internal static func settingsKeyBackupInfoAlgorithm(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_algorithm", p1) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index e1e646d07..332139a0f 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -26,6 +26,7 @@ import UIKit func settingsKeyBackupTableViewSectionShowKeyBackupSetup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showVerifyDevice deviceId:String) func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion) } @objc class SettingsKeyBackupTableViewSection: NSObject { @@ -56,6 +57,10 @@ import UIKit self.viewModel.process(viewAction: .load) } + @objc func delete(keyBackupVersion: MXKeyBackupVersion) { + self.viewModel.process(viewAction: .delete(keyBackupVersion)) + } + // MARK: - Private private func render(viewState: SettingsKeyBackupViewState) { @@ -281,13 +286,12 @@ import UIKit } let deleteCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: fromCellIndex + 1) - let deleteTitle = "Delete backup" + let deleteTitle = VectorL10n.settingsKeyBackupButtonDelete deleteCell.mxkButton.setTitle(deleteTitle, for: .normal) deleteCell.mxkButton.setTitle(deleteTitle, for: .highlighted) deleteCell.mxkButton.tintColor = ThemeService.shared().theme.warningColor deleteCell.mxkButton.vc_addAction { - // TODO: Ask confirmation - self.viewModel.process(viewAction: .delete(keyBackupVersion)) + self.viewModel.process(viewAction: .confirmDelete(keyBackupVersion)) } return [restoreCell, deleteCell] @@ -309,4 +313,7 @@ extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDeleg func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) { self.delegate?.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) } + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion) + } } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift index aab4be8e4..f1748bf67 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift @@ -21,5 +21,6 @@ enum SettingsKeyBackupViewAction { case create case verify(String) case restore(MXKeyBackupVersion) + case confirmDelete(MXKeyBackupVersion) case delete(MXKeyBackupVersion) } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift index d93497fb7..ee5abb4d9 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -55,6 +55,9 @@ class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { case .restore(let keyBackupVersion): viewDelegate.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) break + case .confirmDelete(let keyBackupVersion): + viewDelegate.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion) + break case .delete(let keyBackupVersion): self.deleteKeyBackupVersion(keyBackupVersion) break diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift index 41b03dd77..9fb17cdd4 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -22,6 +22,7 @@ protocol SettingsKeyBackupViewModelViewDelegate: class { func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showVerifyDevice deviceId:String) func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion) } protocol SettingsKeyBackupViewModelType { diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 451c16661..497be828e 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -4304,6 +4304,36 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> [self showKeyBackupRecover:keyBackupVersion]; } +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion +{ + MXWeakify(self); + [currentAlert dismissViewControllerAnimated:NO completion:nil]; + + currentAlert = + [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil) + message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + + [self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion]; + }]]; + + [currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"]; + [self presentViewController:currentAlert animated:YES completion:nil]; +} + #pragma mark - MXKEncryptionInfoView - (void)showDeviceInfo:(MXDeviceInfo*)deviceInfo From 428c001812347acc6e805b35d1fae377c8b17f47 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 10:19:27 +0100 Subject: [PATCH 03/12] key backup settings: Make it display loading wheel and error --- .../SettingsKeyBackupTableViewSection.swift | 14 ++++++++++++++ .../KeyBackup/SettingsKeyBackupViewModel.swift | 16 ++++++++++++---- .../SettingsKeyBackupViewModelType.swift | 1 + .../KeyBackup/SettingsKeyBackupViewState.swift | 8 ++++++++ Riot/Modules/Settings/SettingsViewController.m | 17 +++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index 332139a0f..7e489fba1 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -27,6 +27,9 @@ import UIKit func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showVerifyDevice deviceId:String) func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion) + + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showActivityIndicator show:Bool) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error:Error) } @objc class SettingsKeyBackupTableViewSection: NSObject { @@ -303,6 +306,17 @@ extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDeleg func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) { self.render(viewState: viewSate) } + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) { + switch networkRequestViewSate { + case .loading: + self.delegate?.settingsKeyBackup(self, showActivityIndicator: true) + case .loaded: + self.delegate?.settingsKeyBackup(self, showActivityIndicator: false) + case .error(let error): + self.delegate?.settingsKeyBackup(self, showError: error) + break + } + } func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) { self.delegate?.settingsKeyBackupTableViewSectionShowKeyBackupSetup(self) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift index ee5abb4d9..2a099fb38 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -135,11 +135,19 @@ class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { return } - self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { + self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateNetworkRequestViewState: .loading) - }) { (Error) in - - } + self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { [weak self] () in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateNetworkRequestViewState: .loaded) + }, failure: { [weak self] error in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateNetworkRequestViewState: .error(error)) + }) } } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift index 9fb17cdd4..7f6264e03 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -18,6 +18,7 @@ import UIKit protocol SettingsKeyBackupViewModelViewDelegate: class { func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showVerifyDevice deviceId:String) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift index 2e1ad30d6..5b3ced337 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift @@ -31,3 +31,11 @@ enum SettingsKeyBackupViewState { case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) case backupButNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) } + +/// State representing a network request made by the module +/// Only SettingsKeyBackupViewAction.delete generates such states +enum SettingsKeyBackupNetworkRequestViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 497be828e..3fbc9b642 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -4334,6 +4334,23 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> [self presentViewController:currentAlert animated:YES completion:nil]; } +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showActivityIndicator:(BOOL)show +{ + if (show) + { + [self startActivityIndicator]; + } + else + { + [self stopActivityIndicator]; + } +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showError:(NSError *)error +{ + [[AppDelegate theDelegate] showErrorAsAlert:error]; +} + #pragma mark - MXKEncryptionInfoView - (void)showDeviceInfo:(MXDeviceInfo*)deviceInfo From 2707971e686090de2a21bc89185872722b7c299d Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 15:45:43 +0100 Subject: [PATCH 04/12] key backup settings: Fix cell reuse mechanism --- .../SettingsKeyBackupTableViewSection.swift | 369 ++++++++++++------ .../SettingsKeyBackupViewModel.swift | 2 +- .../SettingsKeyBackupViewModelType.swift | 2 +- .../SettingsKeyBackupViewState.swift | 2 +- .../Modules/Settings/SettingsViewController.m | 10 +- 5 files changed, 244 insertions(+), 141 deletions(-) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index 7e489fba1..d1d3baa4f 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -37,7 +37,6 @@ import UIKit // MARK: - Properties @objc weak var delegate: SettingsKeyBackupTableViewSectionDelegate? - @objc var tableViewCells: [UITableViewCell] // MARK: Private @@ -45,10 +44,12 @@ import UIKit // whereas this class can be used from objC private var viewModel: SettingsKeyBackupViewModelType! + // Need to know the state to make `cellForRow` deliver cells accordingly + private var viewState: SettingsKeyBackupViewState = .checkingBackup + // MARK: - Public @objc init(withKeyBackup keyBackup: MXKeyBackup) { - self.tableViewCells = [] self.viewModel = SettingsKeyBackupViewModel(keyBackup: keyBackup) super.init() self.viewModel.viewDelegate = self @@ -56,6 +57,55 @@ import UIKit self.viewModel.process(viewAction: .load) } + @objc func numberOfRows() -> Int { + var numberOfRows: Int + + switch self.viewState { + case .checkingBackup: + numberOfRows = self.numberOfCheckingBackupRows() + case .noBackup: + numberOfRows = self.numberOfNoBackupRows() + case .backup(_, _): + numberOfRows = self.numberOfBackupRows() + case .backupAndRunning(_, _, _): + numberOfRows = self.numberOfBackupAndRunningRows() + case .backupNotTrusted(_, let keyBackupVersionTrust): + numberOfRows = self.numberOfBackupNotTrustedRows(keyBackupVersionTrust: keyBackupVersionTrust) + } + + return numberOfRows + } + + @objc func cellForRow(atRow row:Int) -> UITableViewCell { + var cell: UITableViewCell + + switch self.viewState { + case .checkingBackup: + cell = self.renderCheckingBackupCell(atRow:row) + + case .noBackup: + cell = self.renderNoBackupCell(atRow:row) + + case .backup(let keyBackupVersion, let keyBackupVersionTrust): + cell = self.renderBackupCell(atRow: row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust) + + case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): + cell = self.renderBackupAndRunningCell(atRow:row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust, + backupProgress: backupProgress) + + case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + cell = self.renderBackupNotTrustedCell(atRow:row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust) + } + + return cell + } + @objc func reload() { self.viewModel.process(viewAction: .load) } @@ -64,125 +114,171 @@ import UIKit self.viewModel.process(viewAction: .delete(keyBackupVersion)) } - // MARK: - Private - private func render(viewState: SettingsKeyBackupViewState) { + // MARK: - Pseudo TableView datasource - guard let delegate = self.delegate else { - return - } - - switch viewState { - case .checkingBackup: - self.renderLoading() - - case .noBackup: - self.renderNoKeyBackup() - - case .backup(let keyBackupVersion, let keyBackupVersionTrust): - self.renderKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust) - - case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): - self.renderRunningKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust, backupProgress: backupProgress) - - case .backupButNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): - self.renderNotTrustedKeyBackup(keyBackupVersion, keyBackupVersionTrust: keyBackupVersionTrust) - } - - delegate.settingsKeyBackupTableViewSectionDidUpdate(self) + private func numberOfCheckingBackupRows() -> Int { + return 1 } - private func renderLoading() { + private func renderCheckingBackupCell(atRow: Int) -> UITableViewCell { // TODO: loading wheel - self.tableViewCells = [] + return UITableViewCell.init() } - private func renderNoKeyBackup() { + + private func numberOfNoBackupRows() -> Int { + return 1 + } + + private func renderNoBackupCell(atRow: Int) -> UITableViewCell { guard let delegate = self.delegate else { - return + return UITableViewCell.init() } - let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) - infoCell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoNone + let cell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + cell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoNone - self.tableViewCells = [infoCell] - - // Add buttons - self.tableViewCells = [infoCell] + self.buttonCellForCreate(fromCellIndex: self.tableViewCells.count) - } - - private func renderKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust) { - - guard let delegate = self.delegate, - let keyBackupVersionVersion = keyBackupVersion.version else { - return - } - - let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) - - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) - let backupStatus = VectorL10n.settingsKeyBackupInfoValid - let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); - - let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust - infoCell.mxkTextView.text = strings.joined(separator: "\n") - - // Add buttons - self.tableViewCells = [infoCell] + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: self.tableViewCells.count) - } - - private func renderRunningKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust, backupProgress:Progress) { - - guard let delegate = self.delegate, - let keyBackupVersionVersion = keyBackupVersion.version else { - return - } - - let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount - - let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) - - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) - let backupStatus = VectorL10n.settingsKeyBackupInfoValid - let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining)) - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); - - let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust - infoCell.mxkTextView.text = strings.joined(separator: "\n") - - // Add buttons - self.tableViewCells = [infoCell] + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: 1) + return cell } - private func renderNotTrustedKeyBackup(_ keyBackupVersion:MXKeyBackupVersion, keyBackupVersionTrust:MXKeyBackupVersionTrust) { + private func numberOfBackupRows() -> Int { + return 3 + } - guard let delegate = self.delegate, - let keyBackupVersionVersion = keyBackupVersion.version else { - return + private func renderBackupCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() } - let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersionVersion) - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) - let backupStatus = VectorL10n.settingsKeyBackupInfoNotValid - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); - let strings = [version, algorithm, backupStatus] + backupTrust - infoCell.mxkTextView.text = strings.joined(separator: "\n") + let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") - self.tableViewCells = [infoCell] + cell = infoCell + + case 1: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 2: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + private func numberOfBackupAndRunningRows() -> Int { + return 3 + } + + private func renderBackupAndRunningCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust, backupProgress: Progress) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + + let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining)) + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + + let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 2: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + private func numberOfBackupNotTrustedRows(keyBackupVersionTrust: MXKeyBackupVersionTrust) -> Int { + var numberOfRows = 3 + if self.lastNonVerifiedDevice(keyBackupVersionTrust) != nil { + numberOfRows += 1 + } + return numberOfRows + } + + private func renderBackupNotTrustedCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } // Display a verify button for the last non verified device only - let deviceId = self.lastNonVerifiedDevice(keyBackupVersionTrust) - self.tableViewCells = self.tableViewCells + self.buttonCellForVerifyingDevice(deviceId, fromCellIndex: self.tableViewCells.count) + let lastNonVerifiedDevice = self.lastNonVerifiedDevice(keyBackupVersionTrust) - // Add buttons - self.tableViewCells = self.tableViewCells + self.buttonCellsForRestoreAndDelete(keyBackupVersion, fromCellIndex: self.tableViewCells.count) + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + + let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + if let lastNonVerifiedDevice = lastNonVerifiedDevice { + cell = self.buttonCellForVerifyingDevice(lastNonVerifiedDevice, atRow: row) + } + else { + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + } + + case 2: + if lastNonVerifiedDevice != nil { + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + } + else { + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + } + + case 3: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell } @@ -235,77 +331,90 @@ import UIKit return lastNonVerifiedDeviceId } - // MARK: - Cells - private func buttonCellForCreate(fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + // MARK: - Button cells + + private func buttonCellForCreate(atRow row: Int) -> UITableViewCell { guard let delegate = self.delegate else { - return [] + return UITableViewCell.init() } - let verifyCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: self.tableViewCells.count) + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) let btnTitle = "Start a new backup" - verifyCell.mxkButton.setTitle(btnTitle, for: .normal) - verifyCell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) - verifyCell.mxkButton.vc_addAction { + cell.mxkButton.vc_addAction { self.viewModel.process(viewAction: .create) } - return [verifyCell] + return cell } - private func buttonCellForVerifyingDevice(_ deviceId: String?, fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + private func buttonCellForVerifyingDevice(_ deviceId: String, atRow row: Int) -> UITableViewCell { - guard let deviceId = deviceId, let delegate = self.delegate else { - return [] + guard let delegate = self.delegate else { + return UITableViewCell.init() } - let verifyCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: self.tableViewCells.count) + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) let btnTitle = "Verify..." - verifyCell.mxkButton.setTitle(btnTitle, for: .normal) - verifyCell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) - verifyCell.mxkButton.vc_addAction { + cell.mxkButton.vc_addAction { self.viewModel.process(viewAction: .verify(deviceId)) } - return [verifyCell] + return cell } - private func buttonCellsForRestoreAndDelete(_ keyBackupVersion: MXKeyBackupVersion, fromCellIndex: Int = 0) -> [MXKTableViewCellWithButton] { + private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell { guard let delegate = self.delegate else { - return [] + return UITableViewCell.init() } - let restoreCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: fromCellIndex) - let restoreTitle = "Restore backup" - restoreCell.mxkButton.setTitle(restoreTitle, for: .normal) - restoreCell.mxkButton.setTitle(restoreTitle, for: .highlighted) - restoreCell.mxkButton.vc_addAction { + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) + let btnTitle = "Restore backup" + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.vc_addAction { self.viewModel.process(viewAction: .restore(keyBackupVersion)) } + return cell + } - let deleteCell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: fromCellIndex + 1) - let deleteTitle = VectorL10n.settingsKeyBackupButtonDelete - deleteCell.mxkButton.setTitle(deleteTitle, for: .normal) - deleteCell.mxkButton.setTitle(deleteTitle, for: .highlighted) - deleteCell.mxkButton.tintColor = ThemeService.shared().theme.warningColor - deleteCell.mxkButton.vc_addAction { + private func buttonCellForDelete(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) + let btnTitle = VectorL10n.settingsKeyBackupButtonDelete + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor + cell.mxkButton.vc_addAction { self.viewModel.process(viewAction: .confirmDelete(keyBackupVersion)) } - return [restoreCell, deleteCell] + return cell } } + // MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDelegate { - func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) { - self.render(viewState: viewSate) + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) { + self.viewState = viewState + + // The tableview datasource will call `self.cellForRow()` + self.delegate?.settingsKeyBackupTableViewSectionDidUpdate(self) } + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) { switch networkRequestViewSate { case .loading: diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift index 2a099fb38..f04ea144c 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -100,7 +100,7 @@ class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } - viewState = .backupButNotTrusted(keyBackupVersion, keyBackupVersionTrust) + viewState = .backupNotTrusted(keyBackupVersion, keyBackupVersionTrust) case MXKeyBackupStateReadyToBackUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift index 7f6264e03..20c59ba94 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -17,7 +17,7 @@ import UIKit protocol SettingsKeyBackupViewModelViewDelegate: class { - func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewSate: SettingsKeyBackupViewState) + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift index 5b3ced337..9751e402e 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift @@ -29,7 +29,7 @@ enum SettingsKeyBackupViewState { case noBackup case backup(MXKeyBackupVersion, MXKeyBackupVersionTrust) case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) - case backupButNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case backupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) } /// State representing a network request made by the module diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 3fbc9b642..df9e2e907 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -1304,7 +1304,7 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> // Check whether this section is visible. if (self.mainSession.crypto) { - count = keyBackupSection.tableViewCells.count; + count = keyBackupSection.numberOfRows; } } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) @@ -2235,10 +2235,7 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> } else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) { - if (row < keyBackupSection.tableViewCells.count) - { - cell = keyBackupSection.tableViewCells[row]; - } + cell = [keyBackupSection cellForRowAtRow:row]; } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) { @@ -4282,9 +4279,6 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; - [cell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; - cell.mxkButton.accessibilityIdentifier = nil; - return cell; } From b2062f22afacea1ce9e0f629f55fb332afe48b7f Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 16:58:41 +0100 Subject: [PATCH 05/12] key backup settings: Make it look like more to https://github.com/vector-im/riot-meta/issues/251#issuecomment-458566684 1/2 --- Riot/Assets/en.lproj/Vector.strings | 16 ++-- Riot/Generated/Strings.swift | 22 +++-- .../SettingsKeyBackupTableViewSection.swift | 82 ++++++++++++++----- 3 files changed, 87 insertions(+), 33 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f1b8659bf..df7cc8491 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -335,7 +335,7 @@ "settings_flair" = "Show flair where allowed"; "settings_devices" = "DEVICES"; "settings_cryptography" = "CRYPTOGRAPHY"; -"settings_key_backup" = "SECURE MESSAGE"; +"settings_key_backup" = "SECURE MESSAGE RECOVERY"; "settings_deactivate_account" = "DEACTIVATE ACCOUNT"; "settings_sign_out" = "Sign Out"; @@ -421,21 +421,23 @@ "settings_deactivate_my_account" = "Deactivate my account"; -"settings_key_backup_info_none" = "No backup is present"; -"settings_key_backup_info_version" = "Backup version: %@"; +"settings_key_backup_info_none" = "Secure Message Recovery has not been set up."; +"settings_key_backup_info_version" = "Key Backup Version: %@"; "settings_key_backup_info_algorithm" = "Algorithm: %@"; -"settings_key_backup_info_valid" = "This device is using key backup"; -"settings_key_backup_info_not_valid" = "This device is not using key backup"; +"settings_key_backup_info_valid" = "Backup has a valid signature from this device."; +"settings_key_backup_info_not_valid" = "Secure Message Recovery is not active on this device."; "settings_key_backup_info_progress" = "Backing up %@ keys..."; -"settings_key_backup_info_progress_done" = "All keys backed up"; +"settings_key_backup_info_progress_done" = "All keys have been backed up"; // TO DEL "settings_key_backup_info_trust_signature_unknown" = "Backup has a signature from unknown device with ID %@."; "settings_key_backup_info_trust_signature_valid" = "Backup has a valid signature from this device"; "settings_key_backup_info_trust_signature_valid_device_verified" = "Backup has a valid signature from verified device %@"; "settings_key_backup_info_trust_signature_valid_device_unverified" = "Backup has a valid signature from unverified device %@"; "settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has a invalid signature from verified device %@"; "settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has a invalid signature from unverified device %@"; -"settings_key_backup_button_create" = "Start a new backup"; +"settings_key_backup_button_create" = "Set up Secure Message Recovery"; +"settings_key_backup_button_restore" = "Restore backup"; "settings_key_backup_button_delete" = "Delete backup"; +"settings_key_backup_button_verify" = "Verify"; "settings_key_backup_delete_confirmation_prompt_title" = "Delete Backup"; "settings_key_backup_delete_confirmation_prompt_msg" = "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 7f8c68f57..e812c8f08 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1918,11 +1918,11 @@ internal enum VectorL10n { internal static var settingsIgnoredUsers: String { return VectorL10n.tr("Vector", "settings_ignored_users") } - /// SECURE MESSAGE + /// SECURE MESSAGE RECOVERY internal static var settingsKeyBackup: String { return VectorL10n.tr("Vector", "settings_key_backup") } - /// Start a new backup + /// Set up Secure Message Recovery internal static var settingsKeyBackupButtonCreate: String { return VectorL10n.tr("Vector", "settings_key_backup_button_create") } @@ -1930,6 +1930,14 @@ internal enum VectorL10n { internal static var settingsKeyBackupButtonDelete: String { return VectorL10n.tr("Vector", "settings_key_backup_button_delete") } + /// Restore backup + internal static var settingsKeyBackupButtonRestore: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_restore") + } + /// Verify + internal static var settingsKeyBackupButtonVerify: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_verify") + } /// Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history internal static var settingsKeyBackupDeleteConfirmationPromptMsg: String { return VectorL10n.tr("Vector", "settings_key_backup_delete_confirmation_prompt_msg") @@ -1942,11 +1950,11 @@ internal enum VectorL10n { internal static func settingsKeyBackupInfoAlgorithm(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_algorithm", p1) } - /// No backup is present + /// Secure Message Recovery has not been set up. internal static var settingsKeyBackupInfoNone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_none") } - /// This device is not using key backup + /// Secure Message Recovery is not active on this device. internal static var settingsKeyBackupInfoNotValid: String { return VectorL10n.tr("Vector", "settings_key_backup_info_not_valid") } @@ -1954,7 +1962,7 @@ internal enum VectorL10n { internal static func settingsKeyBackupInfoProgress(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_progress", p1) } - /// All keys backed up + /// All keys have been backed up internal static var settingsKeyBackupInfoProgressDone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done") } @@ -1982,11 +1990,11 @@ internal enum VectorL10n { internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1) } - /// This device is using key backup + /// Backup has a valid signature from this device. internal static var settingsKeyBackupInfoValid: String { return VectorL10n.tr("Vector", "settings_key_backup_info_valid") } - /// Backup version: %@ + /// Key Backup Version: %@ internal static func settingsKeyBackupInfoVersion(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_version", p1) } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index d1d3baa4f..7e2f45021 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -128,23 +128,35 @@ import UIKit private func numberOfNoBackupRows() -> Int { - return 1 + return 2 } - private func renderNoBackupCell(atRow: Int) -> UITableViewCell { + private func renderNoBackupCell(atRow row: Int) -> UITableViewCell { guard let delegate = self.delegate else { return UITableViewCell.init() } - let cell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) - cell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoNone + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + infoCell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoNone + + cell = infoCell + + case 1: + cell = self.buttonCellForCreate(atRow: row) + + default: + cell = UITableViewCell.init() + } return cell } private func numberOfBackupRows() -> Int { - return 3 + return 5 } private func renderBackupCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { @@ -157,21 +169,37 @@ import UIKit case 0: let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) let backupStatus = VectorL10n.settingsKeyBackupInfoValid let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); - let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + let strings = [backupStatus, uploadStatus] infoCell.mxkTextView.text = strings.joined(separator: "\n") cell = infoCell case 1: - cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + + let strings = [version, algorithm] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell case 2: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 3: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 4: cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) default: @@ -183,7 +211,7 @@ import UIKit private func numberOfBackupAndRunningRows() -> Int { - return 3 + return 5 } private func renderBackupAndRunningCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust, backupProgress: Progress) -> UITableViewCell { @@ -198,21 +226,37 @@ import UIKit let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) let backupStatus = VectorL10n.settingsKeyBackupInfoValid let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining)) - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); - let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + let strings = [backupStatus, uploadStatus] infoCell.mxkTextView.text = strings.joined(separator: "\n") cell = infoCell case 1: - cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + + let strings = [version, algorithm] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell case 2: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 3: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 4: cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) default: @@ -342,7 +386,7 @@ import UIKit let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) - let btnTitle = "Start a new backup" + let btnTitle = VectorL10n.settingsKeyBackupButtonCreate cell.mxkButton.setTitle(btnTitle, for: .normal) cell.mxkButton.setTitle(btnTitle, for: .highlighted) @@ -361,7 +405,7 @@ import UIKit let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) - let btnTitle = "Verify..." + let btnTitle = VectorL10n.settingsKeyBackupButtonVerify cell.mxkButton.setTitle(btnTitle, for: .normal) cell.mxkButton.setTitle(btnTitle, for: .highlighted) @@ -378,7 +422,7 @@ import UIKit } let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) - let btnTitle = "Restore backup" + let btnTitle = VectorL10n.settingsKeyBackupButtonRestore cell.mxkButton.setTitle(btnTitle, for: .normal) cell.mxkButton.setTitle(btnTitle, for: .highlighted) cell.mxkButton.vc_addAction { From 6de109b332194de1589ee756b453d3939b88324a Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 18:19:20 +0100 Subject: [PATCH 06/12] key backup settings: Make it look like more to https://github.com/vector-im/riot-meta/issues/251#issuecomment-458566684 2/2 Manage not trusted backup --- Riot/Assets/en.lproj/Vector.strings | 16 ++-- Riot/Generated/Strings.swift | 18 +++- .../SettingsKeyBackupTableViewSection.swift | 93 ++++++++++++------- 3 files changed, 85 insertions(+), 42 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index df7cc8491..2bc239527 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -427,13 +427,17 @@ "settings_key_backup_info_valid" = "Backup has a valid signature from this device."; "settings_key_backup_info_not_valid" = "Secure Message Recovery is not active on this device."; "settings_key_backup_info_progress" = "Backing up %@ keys..."; -"settings_key_backup_info_progress_done" = "All keys have been backed up"; // TO DEL -"settings_key_backup_info_trust_signature_unknown" = "Backup has a signature from unknown device with ID %@."; +"settings_key_backup_info_progress_done" = "All keys have been backed up"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "To use Secure Message Recovery on this device, verify %@ now."; +"settings_key_backup_info_not_trusted_fix_action" = "To use Secure Message Recovery on this device, provide your passphrase or recovery key now."; + +"settings_key_backup_info_trust_signature_unknown" = "Backup has a signature from device with ID: %@"; "settings_key_backup_info_trust_signature_valid" = "Backup has a valid signature from this device"; -"settings_key_backup_info_trust_signature_valid_device_verified" = "Backup has a valid signature from verified device %@"; -"settings_key_backup_info_trust_signature_valid_device_unverified" = "Backup has a valid signature from unverified device %@"; -"settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has a invalid signature from verified device %@"; -"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has a invalid signature from unverified device %@"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Backup has a valid signature from %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Backup has a signature from %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has an invalid signature from %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has an invalid signature from %@"; + "settings_key_backup_button_create" = "Set up Secure Message Recovery"; "settings_key_backup_button_restore" = "Restore backup"; "settings_key_backup_button_delete" = "Delete backup"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index e812c8f08..d2d49c760 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1954,6 +1954,14 @@ internal enum VectorL10n { internal static var settingsKeyBackupInfoNone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_none") } + /// To use Secure Message Recovery on this device, provide your passphrase or recovery key now. + internal static var settingsKeyBackupInfoNotTrustedFixAction: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_not_trusted_fix_action") + } + /// To use Secure Message Recovery on this device, verify %@ now. + internal static func settingsKeyBackupInfoNotTrustedFromVerifiableDeviceFixAction(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_not_trusted_from_verifiable_device_fix_action", p1) + } /// Secure Message Recovery is not active on this device. internal static var settingsKeyBackupInfoNotValid: String { return VectorL10n.tr("Vector", "settings_key_backup_info_not_valid") @@ -1966,15 +1974,15 @@ internal enum VectorL10n { internal static var settingsKeyBackupInfoProgressDone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done") } - /// Backup has a invalid signature from unverified device %@ + /// Backup has an invalid signature from %@ internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_unverified", p1) } - /// Backup has a invalid signature from verified device %@ + /// Backup has an invalid signature from %@ internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_verified", p1) } - /// Backup has a signature from unknown device with ID %@. + /// Backup has a signature from device with ID: %@ internal static func settingsKeyBackupInfoTrustSignatureUnknown(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_unknown", p1) } @@ -1982,11 +1990,11 @@ internal enum VectorL10n { internal static var settingsKeyBackupInfoTrustSignatureValid: String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid") } - /// Backup has a valid signature from unverified device %@ + /// Backup has a signature from %@ internal static func settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_unverified", p1) } - /// Backup has a valid signature from verified device %@ + /// Backup has a valid signature from %@ internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1) } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index 7e2f45021..01319c60b 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -69,8 +69,8 @@ import UIKit numberOfRows = self.numberOfBackupRows() case .backupAndRunning(_, _, _): numberOfRows = self.numberOfBackupAndRunningRows() - case .backupNotTrusted(_, let keyBackupVersionTrust): - numberOfRows = self.numberOfBackupNotTrustedRows(keyBackupVersionTrust: keyBackupVersionTrust) + case .backupNotTrusted(_, _): + numberOfRows = self.numberOfBackupNotTrustedRows() } return numberOfRows @@ -267,12 +267,8 @@ import UIKit } - private func numberOfBackupNotTrustedRows(keyBackupVersionTrust: MXKeyBackupVersionTrust) -> Int { - var numberOfRows = 3 - if self.lastNonVerifiedDevice(keyBackupVersionTrust) != nil { - numberOfRows += 1 - } - return numberOfRows + private func numberOfBackupNotTrustedRows() -> Int { + return 6 } private func renderBackupNotTrustedCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { @@ -280,42 +276,66 @@ import UIKit return UITableViewCell.init() } - // Display a verify button for the last non verified device only + // Is the device that created the device verifiable? + // ie, is it known and already stored in crytpo store? let lastNonVerifiedDevice = self.lastNonVerifiedDevice(keyBackupVersionTrust) + let lastUnVerifiableDevice = self.lastUnVerifiableDevice(keyBackupVersionTrust) var cell: UITableViewCell switch row { case 0: let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) - let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") - let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) - let backupStatus = VectorL10n.settingsKeyBackupInfoValid - let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone - let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + let backupStatus = VectorL10n.settingsKeyBackupInfoNotValid - let strings = [version, algorithm, backupStatus, uploadStatus] + backupTrust + var fixAction: [String] = [] + if let lastNonVerifiedDevice = lastNonVerifiedDevice { + let deviceName = lastNonVerifiedDevice.displayName ?? lastNonVerifiedDevice.deviceId ?? "" + fixAction = [VectorL10n.settingsKeyBackupInfoNotTrustedFromVerifiableDeviceFixAction(deviceName)] + } + else if lastUnVerifiableDevice != nil { + fixAction = [VectorL10n.settingsKeyBackupInfoNotTrustedFixAction] + } + + let strings = [backupStatus] + fixAction infoCell.mxkTextView.text = strings.joined(separator: "\n") cell = infoCell case 1: if let lastNonVerifiedDevice = lastNonVerifiedDevice { - cell = self.buttonCellForVerifyingDevice(lastNonVerifiedDevice, atRow: row) + cell = self.buttonCellForVerifyingDevice(lastNonVerifiedDevice.deviceId, atRow: row) + } + else if lastUnVerifiableDevice != nil { + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row, title: VectorL10n.settingsKeyBackupButtonVerify) } else { - cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + cell = UITableViewCell.init() } case 2: - if lastNonVerifiedDevice != nil { - cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) - } - else { - cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) - } + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + + let strings = [version, algorithm] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell case 3: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 4: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 5: cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) default: @@ -357,9 +377,9 @@ import UIKit } } - private func lastNonVerifiedDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> String? + private func lastNonVerifiedDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> MXDeviceInfo? { - var lastNonVerifiedDeviceId: String? + var lastNonVerifiedDevice: MXDeviceInfo? for signature in keyBackupVersionTrust.signatures.reversed() { guard let device = signature.device else { @@ -368,13 +388,25 @@ import UIKit if device.verified != MXDeviceVerified { - lastNonVerifiedDeviceId = device.deviceId + lastNonVerifiedDevice = device break } } - return lastNonVerifiedDeviceId + return lastNonVerifiedDevice } + private func lastUnVerifiableDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> String? + { + var lastUnVerifiableDevice: String? + for signature in keyBackupVersionTrust.signatures.reversed() { + + if signature.device == nil { + lastUnVerifiableDevice = signature.deviceId + break + } + } + return lastUnVerifiableDevice + } // MARK: - Button cells @@ -416,15 +448,14 @@ import UIKit return cell } - private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell { + private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, atRow row: Int, title: String = VectorL10n.settingsKeyBackupButtonRestore) -> UITableViewCell { guard let delegate = self.delegate else { return UITableViewCell.init() } let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) - let btnTitle = VectorL10n.settingsKeyBackupButtonRestore - cell.mxkButton.setTitle(btnTitle, for: .normal) - cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.setTitle(title, for: .normal) + cell.mxkButton.setTitle(title, for: .highlighted) cell.mxkButton.vc_addAction { self.viewModel.process(viewAction: .restore(keyBackupVersion)) } From 42a91d7aa8b815fea9428aa2e007f2e564a847a0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 18:41:00 +0100 Subject: [PATCH 07/12] key backup settings: Manage backup created from the device --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- .../KeyBackup/SettingsKeyBackupTableViewSection.swift | 7 +++++-- Riot/Modules/Settings/SettingsViewController.m | 10 ++++++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 2bc239527..c069c110b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -424,7 +424,7 @@ "settings_key_backup_info_none" = "Secure Message Recovery has not been set up."; "settings_key_backup_info_version" = "Key Backup Version: %@"; "settings_key_backup_info_algorithm" = "Algorithm: %@"; -"settings_key_backup_info_valid" = "Backup has a valid signature from this device."; +"settings_key_backup_info_valid" = "Secure Message Recovery has been correctly set up for this device."; "settings_key_backup_info_not_valid" = "Secure Message Recovery is not active on this device."; "settings_key_backup_info_progress" = "Backing up %@ keys..."; "settings_key_backup_info_progress_done" = "All keys have been backed up"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index d2d49c760..e6097da24 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1998,7 +1998,7 @@ internal enum VectorL10n { internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1) } - /// Backup has a valid signature from this device. + /// Secure Message Recovery has been correctly set up for this device. internal static var settingsKeyBackupInfoValid: String { return VectorL10n.tr("Vector", "settings_key_backup_info_valid") } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index 01319c60b..b0068586a 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -47,10 +47,13 @@ import UIKit // Need to know the state to make `cellForRow` deliver cells accordingly private var viewState: SettingsKeyBackupViewState = .checkingBackup + private var userDevice: MXDeviceInfo + // MARK: - Public - @objc init(withKeyBackup keyBackup: MXKeyBackup) { + @objc init(withKeyBackup keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) { self.viewModel = SettingsKeyBackupViewModel(keyBackup: keyBackup) + self.userDevice = userDevice super.init() self.viewModel.viewDelegate = self @@ -357,7 +360,7 @@ import UIKit let displayName = device.displayName ?? device.deviceId ?? "" - if device.fingerprint == "" { // TODO + if device.fingerprint == self.userDevice.fingerprint { return VectorL10n.settingsKeyBackupInfoTrustSignatureValid } else if signature.valid && (device.verified == MXDeviceVerified) { diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index df9e2e907..13c295a0e 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -328,8 +328,14 @@ KeyBackupRecoverCoordinatorBridgePresenterDelegate> if (self.mainSession.crypto.backup) { - keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup]; - keyBackupSection.delegate = self; + MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId + deviceId:self.mainSession.matrixRestClient.credentials.deviceId]; + + if (deviceInfo) + { + keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; + keyBackupSection.delegate = self; + } } groupsDataSource = [[GroupsDataSource alloc] initWithMatrixSession:self.mainSession]; From b53c6f77caafb08a8311c3957d1f3920e53360bb Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 18:48:35 +0100 Subject: [PATCH 08/12] key backup settings: Manage checking state --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ .../SettingsKeyBackupTableViewSection.swift | 12 +++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index c069c110b..6b4e7f22b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -421,6 +421,7 @@ "settings_deactivate_my_account" = "Deactivate my account"; +"settings_key_backup_info_checking" = "Checking..."; "settings_key_backup_info_none" = "Secure Message Recovery has not been set up."; "settings_key_backup_info_version" = "Key Backup Version: %@"; "settings_key_backup_info_algorithm" = "Algorithm: %@"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index e6097da24..9878f73fa 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1950,6 +1950,10 @@ internal enum VectorL10n { internal static func settingsKeyBackupInfoAlgorithm(_ p1: String) -> String { return VectorL10n.tr("Vector", "settings_key_backup_info_algorithm", p1) } + /// Checking... + internal static var settingsKeyBackupInfoChecking: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_checking") + } /// Secure Message Recovery has not been set up. internal static var settingsKeyBackupInfoNone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_none") diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index b0068586a..e6b04bcfc 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -124,9 +124,15 @@ import UIKit return 1 } - private func renderCheckingBackupCell(atRow: Int) -> UITableViewCell { - // TODO: loading wheel - return UITableViewCell.init() + private func renderCheckingBackupCell(atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + cell.mxkTextView.text = VectorL10n.settingsKeyBackupInfoChecking + + return cell } From 25c69eb44c3221e11d36ba8b31274b4afd5875f9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 20:00:38 +0100 Subject: [PATCH 09/12] Key backup: Fix some recovery setup bugs --- .../Setup/KeyBackupSetupCoordinatorBridgePresenter.swift | 2 +- .../Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift | 3 +-- .../RecoveryKey/KeyBackupSetupRecoveryKeyCoordinator.swift | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift index fc38b477e..6a0fdb48e 100644 --- a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift @@ -72,6 +72,6 @@ extension KeyBackupSetupCoordinatorBridgePresenter: KeyBackupSetupCoordinatorDel } func keyBackupSetupCoordinatorDidSetupRecoveryKey(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) { - self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(self) + self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey(self) } } diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift index 9b2d74f13..704c0b614 100644 --- a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift @@ -38,8 +38,7 @@ final class KeyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordin init(session: MXSession) { self.session = session - let keyBackup = MXKeyBackup(matrixSession: session) - let keyBackupSetupPassphraseViewModel = KeyBackupSetupPassphraseViewModel(keyBackup: keyBackup) + let keyBackupSetupPassphraseViewModel = KeyBackupSetupPassphraseViewModel(keyBackup: self.session.crypto.backup) let keyBackupSetupPassphraseViewController = KeyBackupSetupPassphraseViewController.instantiate(with: keyBackupSetupPassphraseViewModel) self.keyBackupSetupPassphraseViewModel = keyBackupSetupPassphraseViewModel self.keyBackupSetupPassphraseViewController = keyBackupSetupPassphraseViewController diff --git a/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyCoordinator.swift b/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyCoordinator.swift index fdc2104b2..6039a24dd 100644 --- a/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyCoordinator.swift +++ b/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyCoordinator.swift @@ -35,8 +35,7 @@ final class KeyBackupSetupRecoveryKeyCoordinator: KeyBackupSetupRecoveryKeyCoord // MARK: - Setup init(session: MXSession, megolmBackupCreationInfo: MXMegolmBackupCreationInfo) { - let keyBackup = MXKeyBackup(matrixSession: session) - let keyBackupSetupRecoveryKeyViewModel = KeyBackupSetupRecoveryKeyViewModel(keyBackup: keyBackup, megolmBackupCreationInfo: megolmBackupCreationInfo) + let keyBackupSetupRecoveryKeyViewModel = KeyBackupSetupRecoveryKeyViewModel(keyBackup: session.crypto.backup, megolmBackupCreationInfo: megolmBackupCreationInfo) let keyBackupSetupRecoveryKeyViewController = KeyBackupSetupRecoveryKeyViewController.instantiate(with: keyBackupSetupRecoveryKeyViewModel) self.keyBackupSetupRecoveryKeyViewModel = keyBackupSetupRecoveryKeyViewModel self.keyBackupSetupRecoveryKeyViewController = keyBackupSetupRecoveryKeyViewController From 2df27b3460f640efe8e42a7ee9b257dcacceb196 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 20:02:01 +0100 Subject: [PATCH 10/12] CHANGES.rst --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 325cbf2c4..8079b8088 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,7 @@ Improvements: * Clean up iOS version checking (#2190). * Key backup: Implement setup screen (#2198). * Key backup: Implement recover screen (#2196). + * Key backup: Add a dedicated section to settings (#2193). Bug fix: * Use white scroll bar on dark themes (#2158). From 0bbf046ed4e5e5d10635fd8fcd4f36ab55d747f3 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 21:15:50 +0100 Subject: [PATCH 11/12] key backup settings: Fix Steve's comment --- .../KeyBackup/SettingsKeyBackupTableViewSection.swift | 9 ++++----- .../Settings/KeyBackup/SettingsKeyBackupViewModel.swift | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index e6b04bcfc..3bed6ca45 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -32,7 +32,7 @@ import UIKit func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error:Error) } -@objc class SettingsKeyBackupTableViewSection: NSObject { +@objc final class SettingsKeyBackupTableViewSection: NSObject { // MARK: - Properties @@ -68,11 +68,11 @@ import UIKit numberOfRows = self.numberOfCheckingBackupRows() case .noBackup: numberOfRows = self.numberOfNoBackupRows() - case .backup(_, _): + case .backup: numberOfRows = self.numberOfBackupRows() - case .backupAndRunning(_, _, _): + case .backupAndRunning: numberOfRows = self.numberOfBackupAndRunningRows() - case .backupNotTrusted(_, _): + case .backupNotTrusted: numberOfRows = self.numberOfBackupNotTrustedRows() } @@ -507,7 +507,6 @@ extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDeleg self.delegate?.settingsKeyBackup(self, showActivityIndicator: false) case .error(let error): self.delegate?.settingsKeyBackup(self, showError: error) - break } } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift index f04ea144c..8187fc89f 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -16,7 +16,7 @@ import UIKit -class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { +final class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { // MARK: - Properties weak var viewDelegate: SettingsKeyBackupViewModelViewDelegate? @@ -44,7 +44,7 @@ class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { switch viewAction { case .load: - self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) + viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) self.checkKeyBackupState() case .create: viewDelegate.settingsKeyBackupViewModelShowKeyBackupSetup(self) @@ -66,7 +66,7 @@ class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { // MARK: - Private - func checkKeyBackupState() { + private func checkKeyBackupState() { if let keyBackupVersion = self.keyBackup.keyBackupVersion { From 3599d97e148d2b718e4c9a8c543a941e94ca9a81 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 31 Jan 2019 21:22:10 +0100 Subject: [PATCH 12/12] key backup settings: Fix Steve's comment --- .../Settings/KeyBackup/SettingsKeyBackupViewModel.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift index 8187fc89f..ab80e90ab 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -44,7 +44,7 @@ final class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { switch viewAction { case .load: - viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) + viewDelegate.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) self.checkKeyBackupState() case .create: viewDelegate.settingsKeyBackupViewModelShowKeyBackupSetup(self) @@ -84,7 +84,7 @@ final class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { } } - func computeState(withBackupVersionTrust keyBackupVersionTrust:MXKeyBackupVersionTrust? = nil) { + private func computeState(withBackupVersionTrust keyBackupVersionTrust:MXKeyBackupVersionTrust? = nil) { var viewState: SettingsKeyBackupViewState? switch self.keyBackup.state { @@ -130,7 +130,7 @@ final class SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { } } - func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) { + private func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) { guard let keyBackupVersionVersion = keyBackupVersion.version else { return }