diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index b48a570ad..fcdfe7e0d 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -210,6 +210,14 @@ B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */; }; B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B142317822CCFA2000FFA96A /* EditHistoryCell.swift */; }; B142317B22CCFA2000FFA96A /* EditHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B142317922CCFA2000FFA96A /* EditHistoryCell.xib */; }; + B14B17602462C69000C2751E /* KeyVerificationManuallyVerifyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B17582462C68F00C2751E /* KeyVerificationManuallyVerifyViewAction.swift */; }; + B14B17612462C69000C2751E /* KeyVerificationManuallyVerifyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B17592462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinator.swift */; }; + B14B17622462C69000C2751E /* KeyVerificationManuallyVerifyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B175A2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.swift */; }; + B14B17632462C69000C2751E /* KeyVerificationManuallyVerifyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14B175B2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.storyboard */; }; + B14B17642462C69000C2751E /* KeyVerificationManuallyVerifyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B175C2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModelType.swift */; }; + B14B17652462C69000C2751E /* KeyVerificationManuallyVerifyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B175D2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModel.swift */; }; + B14B17662462C69000C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B175E2462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift */; }; + B14B17672462C69000C2751E /* KeyVerificationManuallyVerifyViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B175F2462C68F00C2751E /* KeyVerificationManuallyVerifyViewState.swift */; }; B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */; }; B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */; }; B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */; }; @@ -1004,6 +1012,14 @@ B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = ""; }; B142317822CCFA2000FFA96A /* EditHistoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryCell.swift; sourceTree = ""; }; B142317922CCFA2000FFA96A /* EditHistoryCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryCell.xib; sourceTree = ""; }; + B14B17582462C68F00C2751E /* KeyVerificationManuallyVerifyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyViewAction.swift; sourceTree = ""; }; + B14B17592462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyCoordinator.swift; sourceTree = ""; }; + B14B175A2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyViewController.swift; sourceTree = ""; }; + B14B175B2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyVerificationManuallyVerifyViewController.storyboard; sourceTree = ""; }; + B14B175C2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyViewModelType.swift; sourceTree = ""; }; + B14B175D2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyViewModel.swift; sourceTree = ""; }; + B14B175E2462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyCoordinatorType.swift; sourceTree = ""; }; + B14B175F2462C68F00C2751E /* KeyVerificationManuallyVerifyViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationManuallyVerifyViewState.swift; sourceTree = ""; }; B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromRecoveryKeyViewController.storyboard; sourceTree = ""; }; B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModelType.swift; sourceTree = ""; }; B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift; sourceTree = ""; }; @@ -2312,6 +2328,21 @@ path = Start; sourceTree = ""; }; + B14B17572462C68F00C2751E /* ManuallyVerify */ = { + isa = PBXGroup; + children = ( + B14B175E2462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift */, + B14B17592462C68F00C2751E /* KeyVerificationManuallyVerifyCoordinator.swift */, + B14B17582462C68F00C2751E /* KeyVerificationManuallyVerifyViewAction.swift */, + B14B175F2462C68F00C2751E /* KeyVerificationManuallyVerifyViewState.swift */, + B14B175C2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModelType.swift */, + B14B175D2462C68F00C2751E /* KeyVerificationManuallyVerifyViewModel.swift */, + B14B175A2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.swift */, + B14B175B2462C68F00C2751E /* KeyVerificationManuallyVerifyViewController.storyboard */, + ); + path = ManuallyVerify; + sourceTree = ""; + }; B14F142522144F6400FA0595 /* RecoveryKey */ = { isa = PBXGroup; children = ( @@ -2780,6 +2811,7 @@ B18DEDAB243372560075FEF7 /* SelfVerifyWait */, 324A2046225FC571004FE8B0 /* Incoming */, 3232AB96225730E100AD6A5C /* Start */, + B14B17572462C68F00C2751E /* ManuallyVerify */, ); path = Device; sourceTree = ""; @@ -4704,6 +4736,7 @@ B1B5583D20EF6E7F00210D55 /* GroupRoomTableViewCell.xib in Resources */, B1B5572D20EE6C4D00210D55 /* RoomParticipantsViewController.xib in Resources */, 32A6001C22C661100042C1D9 /* EditHistoryViewController.storyboard in Resources */, + B14B17632462C69000C2751E /* KeyVerificationManuallyVerifyViewController.storyboard in Resources */, B1B5577220EE702800210D55 /* JitsiViewController.xib in Resources */, B1B557D720EF5EA900210D55 /* RoomActivitiesView.xib in Resources */, B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */, @@ -5021,6 +5054,7 @@ F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */, B120863722EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift in Sources */, B1B5598620EFC3E000210D55 /* RiotSettings.swift in Sources */, + B14B17622462C69000C2751E /* KeyVerificationManuallyVerifyViewController.swift in Sources */, B1C960F02458308D00C5704B /* RoundedButton.swift in Sources */, B1CE83D52422817200D07506 /* KeyVerificationVerifyByScanningViewController.swift in Sources */, 3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */, @@ -5082,6 +5116,8 @@ B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */, 32863A5A2384070300D07C4A /* RiotSharedSettings.swift in Sources */, B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */, + B14B17652462C69000C2751E /* KeyVerificationManuallyVerifyViewModel.swift in Sources */, + B14B17662462C69000C2751E /* KeyVerificationManuallyVerifyCoordinatorType.swift in Sources */, B10CFBC32268D99D00A5842E /* JitsiService.swift in Sources */, B1A6C10B23882B6C002882FD /* SlidingModalPresentationAnimator.swift in Sources */, B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */, @@ -5310,6 +5346,7 @@ B1B558D020EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B558CF20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */, + B14B17612462C69000C2751E /* KeyVerificationManuallyVerifyCoordinator.swift in Sources */, B183226C23F59F810035B2E8 /* CloseButton.swift in Sources */, B1B5575120EE6C4D00210D55 /* AuthenticationViewController.m in Sources */, B1CE83BA2422815C00D07506 /* KeyVerificationService.swift in Sources */, @@ -5411,6 +5448,7 @@ 6E6F1CAA244DC1E90068B78B /* SectionHeaderView.m in Sources */, B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, + B14B17602462C69000C2751E /* KeyVerificationManuallyVerifyViewAction.swift in Sources */, B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */, 32DB557E22FDADE50016329E /* ServiceTermsModalScreenViewAction.swift in Sources */, 32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */, @@ -5436,10 +5474,12 @@ B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */, B12D79FF23E2462200FACEDC /* UserVerificationStartViewState.swift in Sources */, B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */, + B14B17672462C69000C2751E /* KeyVerificationManuallyVerifyViewState.swift in Sources */, B1B5577D20EE84BF00210D55 /* CircleButton.m in Sources */, 32607D6D243E0A55006674CC /* KeyBackupRecoverFromPrivateKeyCoordinatorType.swift in Sources */, 32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */, B109D6F1222D8C400061B6D9 /* UIApplication.swift in Sources */, + B14B17642462C69000C2751E /* KeyVerificationManuallyVerifyViewModelType.swift in Sources */, B1BEE73723DF44A60003A4CB /* UserVerificationSessionsStatusViewState.swift in Sources */, B108932823ABEE6800802670 /* BubbleCellReadReceiptsDisplayable.swift in Sources */, B1B558FF20EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.m in Sources */, diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 99c5d209e..c9c63b6ca 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1097,6 +1097,16 @@ "key_verification_verify_sas_additional_information" = "For ultimate security, use another trusted means of communication or do this in person."; +// MARK: Manually Verify Device + +"key_verification_manually_verify_device_title" = "Manually Verify by Text"; +"key_verification_manually_verify_device_instruction" = "Confirm by comparing the following with the User Settings in your other session:"; +"key_verification_manually_verify_device_name_title" = "Session name"; +"key_verification_manually_verify_device_id_title" = "Session ID"; +"key_verification_manually_verify_device_key_title" = "Session key"; +"key_verification_manually_verify_device_additional_information" = "If they don't match, the security of your communication may be compromised."; +"key_verification_manually_verify_device_validate_action" = "Verify"; + // Device "device_verification_verify_wait_partner" = "Waiting for partner to confirm…"; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index a756e2933..117fbd353 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -77,6 +77,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: KeyVerificationDataLoadingViewController.self) } + internal enum KeyVerificationManuallyVerifyViewController: StoryboardType { + internal static let storyboardName = "KeyVerificationManuallyVerifyViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyVerificationManuallyVerifyViewController.self) + } internal enum KeyVerificationScanConfirmationViewController: StoryboardType { internal static let storyboardName = "KeyVerificationScanConfirmationViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2ec1f3243..e63e25cc2 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1526,6 +1526,34 @@ internal enum VectorL10n { internal static func keyVerificationIncomingRequestIncomingAlertMessage(_ p1: String) -> String { return VectorL10n.tr("Vector", "key_verification_incoming_request_incoming_alert_message", p1) } + /// If they don't match, the security of your communication may be compromised. + internal static var keyVerificationManuallyVerifyDeviceAdditionalInformation: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_additional_information") + } + /// Session ID + internal static var keyVerificationManuallyVerifyDeviceIdTitle: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_id_title") + } + /// Confirm by comparing the following with the User Settings in your other session: + internal static var keyVerificationManuallyVerifyDeviceInstruction: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_instruction") + } + /// Session key + internal static var keyVerificationManuallyVerifyDeviceKeyTitle: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_key_title") + } + /// Session name + internal static var keyVerificationManuallyVerifyDeviceNameTitle: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_name_title") + } + /// Manually Verify by Text + internal static var keyVerificationManuallyVerifyDeviceTitle: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_title") + } + /// Verify + internal static var keyVerificationManuallyVerifyDeviceValidateAction: String { + return VectorL10n.tr("Vector", "key_verification_manually_verify_device_validate_action") + } /// Verify your new session internal static var keyVerificationNewSessionTitle: String { return VectorL10n.tr("Vector", "key_verification_new_session_title") diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinator.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinator.swift new file mode 100644 index 000000000..6c5f6dafd --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinator.swift @@ -0,0 +1,71 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation +import UIKit + +final class KeyVerificationManuallyVerifyCoordinator: KeyVerificationManuallyVerifyCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var keyVerificationManuallyVerifyViewModel: KeyVerificationManuallyVerifyViewModelType + private let keyVerificationManuallyVerifyViewController: KeyVerificationManuallyVerifyViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyVerificationManuallyVerifyCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, deviceId: String, userId: String) { + self.session = session + + let keyVerificationManuallyVerifyViewModel = KeyVerificationManuallyVerifyViewModel(session: self.session, deviceId: deviceId, userId: userId) + let keyVerificationManuallyVerifyViewController = KeyVerificationManuallyVerifyViewController.instantiate(with: keyVerificationManuallyVerifyViewModel) + self.keyVerificationManuallyVerifyViewModel = keyVerificationManuallyVerifyViewModel + self.keyVerificationManuallyVerifyViewController = keyVerificationManuallyVerifyViewController + } + + // MARK: - Public methods + + func start() { + self.keyVerificationManuallyVerifyViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.keyVerificationManuallyVerifyViewController + } +} + +// MARK: - KeyVerificationManuallyVerifyViewModelCoordinatorDelegate +extension KeyVerificationManuallyVerifyCoordinator: KeyVerificationManuallyVerifyViewModelCoordinatorDelegate { + + func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didVerifiedDeviceWithId deviceId: String, of userId: String) { + self.delegate?.keyVerificationManuallyVerifyCoordinator(self, didVerifiedDeviceWithId: deviceId, of: userId) + } + + func keyVerificationManuallyVerifyViewModelDidCancel(_ viewModel: KeyVerificationManuallyVerifyViewModelType) { + self.delegate?.keyVerificationManuallyVerifyCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift new file mode 100644 index 000000000..7f37b9564 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation + +protocol KeyVerificationManuallyVerifyCoordinatorDelegate: class { + func keyVerificationManuallyVerifyCoordinator(_ coordinator: KeyVerificationManuallyVerifyCoordinatorType, didVerifiedDeviceWithId deviceId: String, of userId: String) + func keyVerificationManuallyVerifyCoordinatorDidCancel(_ coordinator: KeyVerificationManuallyVerifyCoordinatorType) +} + +/// `KeyVerificationManuallyVerifyCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol KeyVerificationManuallyVerifyCoordinatorType: Coordinator, Presentable { + var delegate: KeyVerificationManuallyVerifyCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewAction.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewAction.swift new file mode 100644 index 000000000..e21c9c9a8 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation + +/// KeyVerificationManuallyVerifyViewController view actions exposed to view model +enum KeyVerificationManuallyVerifyViewAction { + case loadData + case verify + case cancel +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.storyboard b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.storyboard new file mode 100644 index 000000000..0c734284b --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.storyboard @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.swift new file mode 100644 index 000000000..d37217b79 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewController.swift @@ -0,0 +1,224 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 KeyVerificationManuallyVerifyViewController: UIViewController { + + // MARK: - Constants + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var deviceNameTitleLabel: UILabel! + @IBOutlet private weak var deviceNameLabel: UILabel! + + @IBOutlet private weak var deviceIdTitleLabel: UILabel! + @IBOutlet private weak var deviceIdLabel: UILabel! + + @IBOutlet private weak var deviceKeyTitleLabel: UILabel! + @IBOutlet private weak var deviceKeyLabel: UILabel! + + @IBOutlet private weak var additionalInformationLabel: UILabel! + + @IBOutlet private weak var verifyButton: RoundedButton! + @IBOutlet private weak var cancelButton: RoundedButton! + + // MARK: Private + + private var viewModel: KeyVerificationManuallyVerifyViewModelType! + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: KeyVerificationManuallyVerifyViewModelType) -> KeyVerificationManuallyVerifyViewController { + let viewController = StoryboardScene.KeyVerificationManuallyVerifyViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.setupViews() + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .loadData) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + if let navigationController = self.navigationController { + if navigationController.navigationBar.isHidden == true { + self.navigationItem.hidesBackButton = true + // Show navigation bar if needed + navigationController.setNavigationBarHidden(false, animated: animated) + } else { + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + // Fix label height after orientation change. See here https://www.objc.io/issues/3-views/advanced-auto-layout-toolbox/#intrinsic-content-size-of-multi-line-text for more information. + self.informationLabel.vc_fixMultilineHeight() + self.deviceNameTitleLabel.vc_fixMultilineHeight() + self.deviceNameLabel.vc_fixMultilineHeight() + self.deviceIdLabel.vc_fixMultilineHeight() + self.deviceIdTitleLabel.vc_fixMultilineHeight() + self.deviceKeyTitleLabel.vc_fixMultilineHeight() + self.deviceKeyLabel.vc_fixMultilineHeight() + self.additionalInformationLabel.vc_fixMultilineHeight() + + self.view.layoutIfNeeded() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.informationLabel.textColor = theme.textPrimaryColor + + self.deviceNameTitleLabel.textColor = theme.textPrimaryColor + self.deviceNameLabel.textColor = theme.textPrimaryColor + + self.deviceIdTitleLabel.textColor = theme.textPrimaryColor + self.deviceIdLabel.textColor = theme.textPrimaryColor + + self.deviceKeyTitleLabel.textColor = theme.textPrimaryColor + self.deviceKeyLabel.textColor = theme.textPrimaryColor + + self.additionalInformationLabel.textColor = theme.textPrimaryColor + + self.cancelButton.update(theme: theme) + self.verifyButton.update(theme: theme) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.title = VectorL10n.keyVerificationManuallyVerifyDeviceTitle + self.informationLabel.text = VectorL10n.keyVerificationManuallyVerifyDeviceInstruction + self.deviceNameTitleLabel.text = VectorL10n.keyVerificationManuallyVerifyDeviceNameTitle + self.deviceIdTitleLabel.text = VectorL10n.keyVerificationManuallyVerifyDeviceIdTitle + self.deviceKeyTitleLabel.text = VectorL10n.keyVerificationManuallyVerifyDeviceKeyTitle + self.additionalInformationLabel.text = VectorL10n.keyVerificationManuallyVerifyDeviceAdditionalInformation + + self.deviceNameLabel.text = nil + self.deviceIdLabel.text = nil + self.deviceKeyLabel.text = nil + + self.cancelButton.actionStyle = .cancel + } + + private func render(viewState: KeyVerificationManuallyVerifyViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded(let viewData): + self.renderLoaded(viewData: viewData) + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded(viewData: KeyVerificationManuallyVerifyViewData) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.deviceNameLabel.text = viewData.deviceName + self.deviceIdLabel.text = viewData.deviceId + self.deviceKeyLabel.text = viewData.deviceKey + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + // MARK: - Actions + + @IBAction private func verifyButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .verify) + } + + @IBAction private func cancelButtonAction(_ sender: Any) { + self.cancelAction() + } + + private func cancelAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - KeyVerificationManuallyVerifyViewModelViewDelegate +extension KeyVerificationManuallyVerifyViewController: KeyVerificationManuallyVerifyViewModelViewDelegate { + + func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didUpdateViewState viewSate: KeyVerificationManuallyVerifyViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModel.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModel.swift new file mode 100644 index 000000000..35f4d8300 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModel.swift @@ -0,0 +1,113 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation + +enum KeyVerificationManuallyVerifyViewModelError: Error { + case unknown + case deviceNotFound +} + +final class KeyVerificationManuallyVerifyViewModel: KeyVerificationManuallyVerifyViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let deviceId: String + private let userId: String + + private var currentOperation: MXHTTPOperation? + + // MARK: Public + + weak var viewDelegate: KeyVerificationManuallyVerifyViewModelViewDelegate? + weak var coordinatorDelegate: KeyVerificationManuallyVerifyViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, deviceId: String, userId: String) { + self.session = session + self.deviceId = deviceId + self.userId = userId + } + + deinit { + self.cancelOperations() + } + + // MARK: - Public + + func process(viewAction: KeyVerificationManuallyVerifyViewAction) { + switch viewAction { + case .loadData: + self.loadData() + case .verify: + self.verifyDevice() + case .cancel: + self.cancelOperations() + self.coordinatorDelegate?.keyVerificationManuallyVerifyViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func loadData() { + + guard let deviceInfo = self.session.crypto.device(withDeviceId: self.deviceId, ofUser: self.userId) else { + self.update(viewState: .error(KeyVerificationManuallyVerifyViewModelError.deviceNotFound)) + return + } + + var deviceKey: String? + + if let deviceFingerprint = deviceInfo.fingerprint { + deviceKey = MXTools.addWhiteSpaces(to: deviceFingerprint, every: 4) + } + + let viewData = KeyVerificationManuallyVerifyViewData(deviceId: self.deviceId, deviceName: deviceInfo.displayName, deviceKey: deviceKey) + self.update(viewState: .loaded(viewData)) + } + + private func update(viewState: KeyVerificationManuallyVerifyViewState) { + self.viewDelegate?.keyVerificationManuallyVerifyViewModel(self, didUpdateViewState: viewState) + } + + private func verifyDevice() { + + self.update(viewState: .loading) + + self.session.crypto.setDeviceVerification(.verified, forDevice: self.deviceId, ofUser: self.userId, success: { [weak self] in + guard let self = self else { + return + } + self.coordinatorDelegate?.keyVerificationManuallyVerifyViewModel(self, didVerifiedDeviceWithId: self.deviceId, of: self.userId) + }, failure: { [weak self] (error) in + guard let self = self else { + return + } + let finalError = error ?? KeyVerificationManuallyVerifyViewModelError.unknown + self.update(viewState: .error(finalError)) + }) + } + + private func cancelOperations() { + self.currentOperation?.cancel() + } +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift new file mode 100644 index 000000000..49f1972a5 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation + +protocol KeyVerificationManuallyVerifyViewModelViewDelegate: class { + func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didUpdateViewState viewSate: KeyVerificationManuallyVerifyViewState) +} + +protocol KeyVerificationManuallyVerifyViewModelCoordinatorDelegate: class { + func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didVerifiedDeviceWithId deviceId: String, of userId: String) + func keyVerificationManuallyVerifyViewModelDidCancel(_ viewModel: KeyVerificationManuallyVerifyViewModelType) +} + +/// Protocol describing the view model used by `KeyVerificationManuallyVerifyViewController` +protocol KeyVerificationManuallyVerifyViewModelType { + + var viewDelegate: KeyVerificationManuallyVerifyViewModelViewDelegate? { get set } + var coordinatorDelegate: KeyVerificationManuallyVerifyViewModelCoordinatorDelegate? { get set } + + func process(viewAction: KeyVerificationManuallyVerifyViewAction) +} diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewState.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewState.swift new file mode 100644 index 000000000..0c272bfb7 --- /dev/null +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewState.swift @@ -0,0 +1,32 @@ +// File created from ScreenTemplate +// $ createScreen.sh KeyVerification/Device/ManuallyVerify KeyVerificationManuallyVerify +/* + Copyright 2020 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 Foundation + +struct KeyVerificationManuallyVerifyViewData { + let deviceId: String + let deviceName: String? + let deviceKey: String? +} + +/// KeyVerificationManuallyVerifyViewController view state +enum KeyVerificationManuallyVerifyViewState { + case loading + case loaded(_ viewData: KeyVerificationManuallyVerifyViewData) + case error(Error) +}