Merge pull request #2210 from vector-im/riot_2173

key backup: Add a dedicated section to settings
This commit is contained in:
manuroe
2019-01-31 21:23:27 +01:00
committed by GitHub
14 changed files with 1214 additions and 11 deletions
+1
View File
@@ -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).
* Key backup: Implement setup reminder (#2211).
Bug fix:
+32
View File
@@ -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 */; };
@@ -472,6 +478,7 @@
3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = "<group>"; };
3267EFB620E379FD00FF1CAA /* README.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.rst; sourceTree = "<group>"; };
3275FD8B21A5A2C500B9C13D /* TermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = "<group>"; };
3281BCF62201FA4200F4A383 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
3284A35020A07C210044F922 /* postMessageAPI.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = postMessageAPI.js; sourceTree = "<group>"; };
32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = "<group>"; };
32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -480,6 +487,11 @@
32BDC9A4211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/InfoPlist.strings; sourceTree = "<group>"; };
32BDC9A5211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = "<group>"; };
32BDC9A6211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Vector.strings; sourceTree = "<group>"; };
32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModel.swift; sourceTree = "<group>"; };
32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModelType.swift; sourceTree = "<group>"; };
32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewState.swift; sourceTree = "<group>"; };
32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewAction.swift; sourceTree = "<group>"; };
32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupTableViewSection.swift; sourceTree = "<group>"; };
32D7159E2146CC6F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Vector.strings; sourceTree = "<group>"; };
32D7159F2146CC7F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
32D715A02146CC8800DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -1166,6 +1178,18 @@
path = js;
sourceTree = "<group>";
};
32BF994D21FA1C6300698084 /* KeyBackup */ = {
isa = PBXGroup;
children = (
32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */,
32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */,
32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */,
32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */,
32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */,
);
path = KeyBackup;
sourceTree = "<group>";
};
5FC42FA41F5186AFFB6A2404 /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -1624,6 +1648,7 @@
B1B5567F20EE6C4C00210D55 /* SettingsViewController.h */,
B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */,
B1B5578120EF564900210D55 /* Views */,
32BF994D21FA1C6300698084 /* KeyBackup */,
B1B5567B20EE6C4C00210D55 /* Language */,
B1B5567820EE6C4C00210D55 /* PhoneCountry */,
B1B5568020EE6C4C00210D55 /* DeactivateAccount */,
@@ -2826,6 +2851,7 @@
B1CA3A2821EF692B000D1D89 /* UIView.swift */,
B140B4A121F87F7100E3F5FE /* OperationQueue.swift */,
B1E5368821FB1E20001F3AFF /* UIButton.swift */,
3281BCF62201FA4200F4A383 /* UIControl.swift */,
);
path = Categories;
sourceTree = "<group>";
@@ -3451,6 +3477,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 */,
@@ -3602,8 +3629,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 */,
@@ -3611,6 +3641,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 */,
@@ -3622,6 +3653,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 */,
+26
View File
@@ -335,6 +335,7 @@
"settings_flair" = "Show flair where allowed";
"settings_devices" = "DEVICES";
"settings_cryptography" = "CRYPTOGRAPHY";
"settings_key_backup" = "SECURE MESSAGE RECOVERY";
"settings_deactivate_account" = "DEACTIVATE ACCOUNT";
"settings_sign_out" = "Sign Out";
@@ -420,6 +421,31 @@
"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: %@";
"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";
"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 %@";
"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";
"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";
// Room Details
"room_details_title" = "Room Details";
"room_details_people" = "Members";
+38
View File
@@ -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)
}
}
+92
View File
@@ -1926,6 +1926,98 @@ internal enum VectorL10n {
internal static var settingsIgnoredUsers: String {
return VectorL10n.tr("Vector", "settings_ignored_users")
}
/// SECURE MESSAGE RECOVERY
internal static var settingsKeyBackup: String {
return VectorL10n.tr("Vector", "settings_key_backup")
}
/// Set up Secure Message Recovery
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")
}
/// 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")
}
/// 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)
}
/// 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")
}
/// 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")
}
/// Backing up %@ keys...
internal static func settingsKeyBackupInfoProgress(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_key_backup_info_progress", p1)
}
/// All keys have been backed up
internal static var settingsKeyBackupInfoProgressDone: String {
return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done")
}
/// 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 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 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 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 %@
internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1)
}
/// 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")
}
/// Key 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")
@@ -72,6 +72,6 @@ extension KeyBackupSetupCoordinatorBridgePresenter: KeyBackupSetupCoordinatorDel
}
func keyBackupSetupCoordinatorDidSetupRecoveryKey(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) {
self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(self)
self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey(self)
}
}
@@ -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
@@ -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
@@ -0,0 +1,525 @@
/*
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)
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion)
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showActivityIndicator show:Bool)
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error:Error)
}
@objc final class SettingsKeyBackupTableViewSection: NSObject {
// MARK: - Properties
@objc weak var delegate: SettingsKeyBackupTableViewSectionDelegate?
// 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!
// 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, userDevice: MXDeviceInfo) {
self.viewModel = SettingsKeyBackupViewModel(keyBackup: keyBackup)
self.userDevice = userDevice
super.init()
self.viewModel.viewDelegate = self
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:
numberOfRows = self.numberOfBackupNotTrustedRows()
}
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)
}
@objc func delete(keyBackupVersion: MXKeyBackupVersion) {
self.viewModel.process(viewAction: .delete(keyBackupVersion))
}
// MARK: - Pseudo TableView datasource
private func numberOfCheckingBackupRows() -> Int {
return 1
}
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
}
private func numberOfNoBackupRows() -> Int {
return 2
}
private func renderNoBackupCell(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell.init()
}
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 5
}
private func renderBackupCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell.init()
}
var cell: UITableViewCell
switch row {
case 0:
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
let backupStatus = VectorL10n.settingsKeyBackupInfoValid
let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone
let strings = [backupStatus, uploadStatus]
infoCell.mxkTextView.text = strings.joined(separator: "\n")
cell = infoCell
case 1:
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:
cell = UITableViewCell.init()
}
return cell
}
private func numberOfBackupAndRunningRows() -> Int {
return 5
}
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 backupStatus = VectorL10n.settingsKeyBackupInfoValid
let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining))
let strings = [backupStatus, uploadStatus]
infoCell.mxkTextView.text = strings.joined(separator: "\n")
cell = infoCell
case 1:
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:
cell = UITableViewCell.init()
}
return cell
}
private func numberOfBackupNotTrustedRows() -> Int {
return 6
}
private func renderBackupNotTrustedCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell.init()
}
// 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 backupStatus = VectorL10n.settingsKeyBackupInfoNotValid
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.deviceId, atRow: row)
}
else if lastUnVerifiableDevice != nil {
cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row, title: VectorL10n.settingsKeyBackupButtonVerify)
}
else {
cell = UITableViewCell.init()
}
case 2:
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:
cell = UITableViewCell.init()
}
return cell
}
// 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 == self.userDevice.fingerprint {
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) -> MXDeviceInfo?
{
var lastNonVerifiedDevice: MXDeviceInfo?
for signature in keyBackupVersionTrust.signatures.reversed() {
guard let device = signature.device else {
continue
}
if device.verified != MXDeviceVerified
{
lastNonVerifiedDevice = device
break
}
}
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
private func buttonCellForCreate(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell.init()
}
let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.settingsKeyBackupButtonCreate
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .create)
}
return cell
}
private func buttonCellForVerifyingDevice(_ deviceId: String, atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell.init()
}
let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.settingsKeyBackupButtonVerify
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .verify(deviceId))
}
return cell
}
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)
cell.mxkButton.setTitle(title, for: .normal)
cell.mxkButton.setTitle(title, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .restore(keyBackupVersion))
}
return cell
}
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 cell
}
}
// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate
extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDelegate {
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:
self.delegate?.settingsKeyBackup(self, showActivityIndicator: true)
case .loaded:
self.delegate?.settingsKeyBackup(self, showActivityIndicator: false)
case .error(let error):
self.delegate?.settingsKeyBackup(self, showError: error)
}
}
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)
}
func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) {
self.delegate?.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion)
}
}
@@ -0,0 +1,26 @@
/*
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 confirmDelete(MXKeyBackupVersion)
case delete(MXKeyBackupVersion)
}
@@ -0,0 +1,153 @@
/*
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
final 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:
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 .confirmDelete(let keyBackupVersion):
viewDelegate.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion)
break
case .delete(let keyBackupVersion):
self.deleteKeyBackupVersion(keyBackupVersion)
break
}
}
// MARK: - Private
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()
}
}
private 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 = .backupNotTrusted(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)
}
}
private func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) {
guard let keyBackupVersionVersion = keyBackupVersion.version else {
return
}
self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateNetworkRequestViewState: .loading)
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))
})
}
}
@@ -0,0 +1,34 @@
/*
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 viewState: SettingsKeyBackupViewState)
func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState)
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 {
var viewDelegate: SettingsKeyBackupViewModelViewDelegate? { get set }
func process(viewAction: SettingsKeyBackupViewAction)
}
@@ -0,0 +1,41 @@
/*
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 backupNotTrusted(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)
}
+243 -6
View File
@@ -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 () <DeactivateAccountViewControllerDelegate>
@interface SettingsViewController () <DeactivateAccountViewControllerDelegate,
SettingsKeyBackupTableViewSectionDelegate,
MXKEncryptionInfoViewDelegate,
KeyBackupSetupCoordinatorBridgePresenterDelegate,
KeyBackupRecoverCoordinatorBridgePresenterDelegate>
{
// 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,19 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
{
[self addMatrixSession:mxSession];
}
if (self.mainSession.crypto.backup)
{
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];
[groupsDataSource finalizeInitialization];
groupsDataSource.delegate = self;
@@ -404,6 +427,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
[super destroy];
}
keyBackupSetupCoordinatorBridgePresenter = nil;
keyBackupRecoverCoordinatorBridgePresenter = nil;
}
- (void)onMatrixSessionStateDidChange:(NSNotification *)notif
@@ -1279,6 +1305,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.numberOfRows;
}
}
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
{
count = 1;
@@ -2205,6 +2239,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
cell = exportKeysBtnCell;
}
}
else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX)
{
cell = [keyBackupSection cellForRowAtRow:row];
}
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
{
MXKTableViewCellWithButton *deactivateAccountBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
@@ -2308,12 +2346,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 +4244,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 +4255,204 @@ 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];
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];
}
- (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];
}
- (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
{
// 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