mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-29 15:52:40 +00:00
Merge pull request #2960 from vector-im/riot_2924
Cross-signing: New room member screen design (WIP)
This commit is contained in:
commit
c2d1442f94
51 changed files with 3271 additions and 108 deletions
|
@ -179,6 +179,15 @@
|
|||
B125FE21231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE20231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift */; };
|
||||
B125FE23231D5E4300B72806 /* SettingsDiscoveryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE22231D5E4300B72806 /* SettingsDiscoveryViewState.swift */; };
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */; };
|
||||
B12D79FB23E2462200FACEDC /* UserVerificationStartCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F323E2462000FACEDC /* UserVerificationStartCoordinator.swift */; };
|
||||
B12D79FC23E2462200FACEDC /* UserVerificationStartViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B12D79F423E2462100FACEDC /* UserVerificationStartViewController.storyboard */; };
|
||||
B12D79FD23E2462200FACEDC /* UserVerificationStartViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F523E2462100FACEDC /* UserVerificationStartViewModelType.swift */; };
|
||||
B12D79FE23E2462200FACEDC /* UserVerificationStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F623E2462100FACEDC /* UserVerificationStartViewController.swift */; };
|
||||
B12D79FF23E2462200FACEDC /* UserVerificationStartViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F723E2462100FACEDC /* UserVerificationStartViewState.swift */; };
|
||||
B12D7A0023E2462200FACEDC /* UserVerificationStartCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F823E2462200FACEDC /* UserVerificationStartCoordinatorType.swift */; };
|
||||
B12D7A0123E2462200FACEDC /* UserVerificationStartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79F923E2462200FACEDC /* UserVerificationStartViewModel.swift */; };
|
||||
B12D7A0223E2462200FACEDC /* UserVerificationStartViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D79FA23E2462200FACEDC /* UserVerificationStartViewAction.swift */; };
|
||||
B12D7A0423E43DCC00FACEDC /* KeyVerificationKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D7A0323E43DCC00FACEDC /* KeyVerificationKind.swift */; };
|
||||
B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */; };
|
||||
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */; };
|
||||
B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */; };
|
||||
|
@ -532,6 +541,27 @@
|
|||
B1BD71BC238E8F9600BA92E2 /* WidgetPermissionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BD71BA238E8F9600BA92E2 /* WidgetPermissionViewController.swift */; };
|
||||
B1BD71BF238EA56700BA92E2 /* WidgetPermissionViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1BD71BE238EA56700BA92E2 /* WidgetPermissionViewController.storyboard */; };
|
||||
B1BD71C1238EA92100BA92E2 /* WidgetPermissionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BD71C0238EA92000BA92E2 /* WidgetPermissionViewModel.swift */; };
|
||||
B1BEE71423DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE71123DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift */; };
|
||||
B1BEE71523DF2ACF0003A4CB /* UserVerificationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE71223DF2ACF0003A4CB /* UserVerificationCoordinator.swift */; };
|
||||
B1BEE71623DF2ACF0003A4CB /* UserVerificationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE71323DF2ACF0003A4CB /* UserVerificationCoordinatorBridgePresenter.swift */; };
|
||||
B1BEE72A23DF38B20003A4CB /* UserVerificationSessionStatusCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE72823DF38B10003A4CB /* UserVerificationSessionStatusCell.swift */; };
|
||||
B1BEE72B23DF38B20003A4CB /* UserVerificationSessionStatusCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1BEE72923DF38B20003A4CB /* UserVerificationSessionStatusCell.xib */; };
|
||||
B1BEE73423DF44A60003A4CB /* UserVerificationSessionsStatusViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE72C23DF44A20003A4CB /* UserVerificationSessionsStatusViewModelType.swift */; };
|
||||
B1BEE73523DF44A60003A4CB /* UserVerificationSessionsStatusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE72D23DF44A30003A4CB /* UserVerificationSessionsStatusViewModel.swift */; };
|
||||
B1BEE73623DF44A60003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE72E23DF44A30003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift */; };
|
||||
B1BEE73723DF44A60003A4CB /* UserVerificationSessionsStatusViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE72F23DF44A40003A4CB /* UserVerificationSessionsStatusViewState.swift */; };
|
||||
B1BEE73823DF44A60003A4CB /* UserVerificationSessionsStatusViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE73023DF44A40003A4CB /* UserVerificationSessionsStatusViewAction.swift */; };
|
||||
B1BEE73923DF44A60003A4CB /* UserVerificationSessionsStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE73123DF44A50003A4CB /* UserVerificationSessionsStatusViewController.swift */; };
|
||||
B1BEE73A23DF44A60003A4CB /* UserVerificationSessionsStatusViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1BEE73223DF44A50003A4CB /* UserVerificationSessionsStatusViewController.storyboard */; };
|
||||
B1BEE73B23DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE73323DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift */; };
|
||||
B1BEE74623E093260003A4CB /* UserVerificationSessionStatusViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE73E23E093220003A4CB /* UserVerificationSessionStatusViewState.swift */; };
|
||||
B1BEE74723E093260003A4CB /* UserVerificationSessionStatusViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE73F23E093230003A4CB /* UserVerificationSessionStatusViewModelType.swift */; };
|
||||
B1BEE74823E093260003A4CB /* UserVerificationSessionStatusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE74023E093230003A4CB /* UserVerificationSessionStatusViewModel.swift */; };
|
||||
B1BEE74923E093260003A4CB /* UserVerificationSessionStatusViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1BEE74123E093230003A4CB /* UserVerificationSessionStatusViewController.storyboard */; };
|
||||
B1BEE74A23E093260003A4CB /* UserVerificationSessionStatusCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE74223E093240003A4CB /* UserVerificationSessionStatusCoordinatorType.swift */; };
|
||||
B1BEE74B23E093260003A4CB /* UserVerificationSessionStatusViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE74323E093250003A4CB /* UserVerificationSessionStatusViewAction.swift */; };
|
||||
B1BEE74C23E093260003A4CB /* UserVerificationSessionStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE74423E093250003A4CB /* UserVerificationSessionStatusViewController.swift */; };
|
||||
B1BEE74D23E093260003A4CB /* UserVerificationSessionStatusCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BEE74523E093260003A4CB /* UserVerificationSessionStatusCoordinator.swift */; };
|
||||
B1C335CD22F1C1320021BA8D /* CameraPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335CC22F1C1320021BA8D /* CameraPresenter.swift */; };
|
||||
B1C3360122F1ED600021BA8D /* MediaPickerCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335FE22F1ED5F0021BA8D /* MediaPickerCoordinatorType.swift */; };
|
||||
B1C3360222F1ED600021BA8D /* MediaPickerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */; };
|
||||
|
@ -878,6 +908,15 @@
|
|||
B125FE20231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewAction.swift; sourceTree = "<group>"; };
|
||||
B125FE22231D5E4300B72806 /* SettingsDiscoveryViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewState.swift; sourceTree = "<group>"; };
|
||||
B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageURLParser.swift; sourceTree = "<group>"; };
|
||||
B12D79F323E2462000FACEDC /* UserVerificationStartCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartCoordinator.swift; sourceTree = "<group>"; };
|
||||
B12D79F423E2462100FACEDC /* UserVerificationStartViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = UserVerificationStartViewController.storyboard; sourceTree = "<group>"; };
|
||||
B12D79F523E2462100FACEDC /* UserVerificationStartViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartViewModelType.swift; sourceTree = "<group>"; };
|
||||
B12D79F623E2462100FACEDC /* UserVerificationStartViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartViewController.swift; sourceTree = "<group>"; };
|
||||
B12D79F723E2462100FACEDC /* UserVerificationStartViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartViewState.swift; sourceTree = "<group>"; };
|
||||
B12D79F823E2462200FACEDC /* UserVerificationStartCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B12D79F923E2462200FACEDC /* UserVerificationStartViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartViewModel.swift; sourceTree = "<group>"; };
|
||||
B12D79FA23E2462200FACEDC /* UserVerificationStartViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationStartViewAction.swift; sourceTree = "<group>"; };
|
||||
B12D7A0323E43DCC00FACEDC /* KeyVerificationKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationKind.swift; sourceTree = "<group>"; };
|
||||
B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModel.swift; sourceTree = "<group>"; };
|
||||
B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModelType.swift; sourceTree = "<group>"; };
|
||||
B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewAction.swift; sourceTree = "<group>"; };
|
||||
|
@ -1416,6 +1455,28 @@
|
|||
B1BD71BA238E8F9600BA92E2 /* WidgetPermissionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPermissionViewController.swift; sourceTree = "<group>"; };
|
||||
B1BD71BE238EA56700BA92E2 /* WidgetPermissionViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = WidgetPermissionViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1BD71C0238EA92000BA92E2 /* WidgetPermissionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPermissionViewModel.swift; sourceTree = "<group>"; };
|
||||
B1BEE71123DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1BEE71223DF2ACF0003A4CB /* UserVerificationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1BEE71323DF2ACF0003A4CB /* UserVerificationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B1BEE72823DF38B10003A4CB /* UserVerificationSessionStatusCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusCell.swift; sourceTree = "<group>"; };
|
||||
B1BEE72923DF38B20003A4CB /* UserVerificationSessionStatusCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UserVerificationSessionStatusCell.xib; sourceTree = "<group>"; };
|
||||
B1BEE72C23DF44A20003A4CB /* UserVerificationSessionsStatusViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusViewModelType.swift; sourceTree = "<group>"; };
|
||||
B1BEE72D23DF44A30003A4CB /* UserVerificationSessionsStatusViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusViewModel.swift; sourceTree = "<group>"; };
|
||||
B1BEE72E23DF44A30003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1BEE72F23DF44A40003A4CB /* UserVerificationSessionsStatusViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusViewState.swift; sourceTree = "<group>"; };
|
||||
B1BEE73023DF44A40003A4CB /* UserVerificationSessionsStatusViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusViewAction.swift; sourceTree = "<group>"; };
|
||||
B1BEE73123DF44A50003A4CB /* UserVerificationSessionsStatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusViewController.swift; sourceTree = "<group>"; };
|
||||
B1BEE73223DF44A50003A4CB /* UserVerificationSessionsStatusViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = UserVerificationSessionsStatusViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1BEE73323DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionsStatusCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1BEE73C23E070300003A4CB /* UserEncryptionTrustLevel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserEncryptionTrustLevel.h; sourceTree = "<group>"; };
|
||||
B1BEE73E23E093220003A4CB /* UserVerificationSessionStatusViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusViewState.swift; sourceTree = "<group>"; };
|
||||
B1BEE73F23E093230003A4CB /* UserVerificationSessionStatusViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusViewModelType.swift; sourceTree = "<group>"; };
|
||||
B1BEE74023E093230003A4CB /* UserVerificationSessionStatusViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusViewModel.swift; sourceTree = "<group>"; };
|
||||
B1BEE74123E093230003A4CB /* UserVerificationSessionStatusViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = UserVerificationSessionStatusViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1BEE74223E093240003A4CB /* UserVerificationSessionStatusCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1BEE74323E093250003A4CB /* UserVerificationSessionStatusViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusViewAction.swift; sourceTree = "<group>"; };
|
||||
B1BEE74423E093250003A4CB /* UserVerificationSessionStatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusViewController.swift; sourceTree = "<group>"; };
|
||||
B1BEE74523E093260003A4CB /* UserVerificationSessionStatusCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserVerificationSessionStatusCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1C335CC22F1C1320021BA8D /* CameraPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPresenter.swift; sourceTree = "<group>"; };
|
||||
B1C335FE22F1ED5F0021BA8D /* MediaPickerCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1690,8 +1751,9 @@
|
|||
324A2046225FC571004FE8B0 /* Incoming */,
|
||||
32891D72226728EE00C82226 /* Loading */,
|
||||
3232AB96225730E100AD6A5C /* Start */,
|
||||
32891D6D2264DF7B00C82226 /* Verified */,
|
||||
3232ABAC2257BE6400AD6A5C /* Verify */,
|
||||
32891D6D2264DF7B00C82226 /* Verified */,
|
||||
B12D7A0323E43DCC00FACEDC /* KeyVerificationKind.swift */,
|
||||
3232AB95225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift */,
|
||||
3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */,
|
||||
3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */,
|
||||
|
@ -2124,6 +2186,21 @@
|
|||
path = RoomMessageLinkParser;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B12D79F223E2426800FACEDC /* Start */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B12D79F323E2462000FACEDC /* UserVerificationStartCoordinator.swift */,
|
||||
B12D79F823E2462200FACEDC /* UserVerificationStartCoordinatorType.swift */,
|
||||
B12D79FA23E2462200FACEDC /* UserVerificationStartViewAction.swift */,
|
||||
B12D79F423E2462100FACEDC /* UserVerificationStartViewController.storyboard */,
|
||||
B12D79F623E2462100FACEDC /* UserVerificationStartViewController.swift */,
|
||||
B12D79F923E2462200FACEDC /* UserVerificationStartViewModel.swift */,
|
||||
B12D79F523E2462100FACEDC /* UserVerificationStartViewModelType.swift */,
|
||||
B12D79F723E2462100FACEDC /* UserVerificationStartViewState.swift */,
|
||||
);
|
||||
path = Start;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B14F142522144F6400FA0595 /* RecoveryKey */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2513,6 +2590,7 @@
|
|||
B1B5567620EE6C4C00210D55 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE71023DF28CA0003A4CB /* UserVerification */,
|
||||
B1A12C64239AB74500AA2B86 /* CrossSigning */,
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */,
|
||||
32DB556722FDADE50016329E /* ServiceTerms */,
|
||||
|
@ -2712,6 +2790,7 @@
|
|||
B1B556A620EE6C4C00210D55 /* Detail */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE73C23E070300003A4CB /* UserEncryptionTrustLevel.h */,
|
||||
B1B556A720EE6C4C00210D55 /* RoomMemberDetailsViewController.h */,
|
||||
B1B556A820EE6C4C00210D55 /* RoomMemberDetailsViewController.m */,
|
||||
B1B556A920EE6C4C00210D55 /* RoomMemberDetailsViewController.xib */,
|
||||
|
@ -3668,6 +3747,51 @@
|
|||
path = ReactionHistory;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1BEE71023DF28CA0003A4CB /* UserVerification */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE71223DF2ACF0003A4CB /* UserVerificationCoordinator.swift */,
|
||||
B1BEE71123DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift */,
|
||||
B1BEE71323DF2ACF0003A4CB /* UserVerificationCoordinatorBridgePresenter.swift */,
|
||||
B12D79F223E2426800FACEDC /* Start */,
|
||||
B1BEE71723DF2B8A0003A4CB /* SessionsStatus */,
|
||||
B1BEE73D23E08AC30003A4CB /* SessionStatus */,
|
||||
);
|
||||
path = UserVerification;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1BEE71723DF2B8A0003A4CB /* SessionsStatus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE73323DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift */,
|
||||
B1BEE72E23DF44A30003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift */,
|
||||
B1BEE73023DF44A40003A4CB /* UserVerificationSessionsStatusViewAction.swift */,
|
||||
B1BEE73223DF44A50003A4CB /* UserVerificationSessionsStatusViewController.storyboard */,
|
||||
B1BEE73123DF44A50003A4CB /* UserVerificationSessionsStatusViewController.swift */,
|
||||
B1BEE72D23DF44A30003A4CB /* UserVerificationSessionsStatusViewModel.swift */,
|
||||
B1BEE72C23DF44A20003A4CB /* UserVerificationSessionsStatusViewModelType.swift */,
|
||||
B1BEE72F23DF44A40003A4CB /* UserVerificationSessionsStatusViewState.swift */,
|
||||
B1BEE72823DF38B10003A4CB /* UserVerificationSessionStatusCell.swift */,
|
||||
B1BEE72923DF38B20003A4CB /* UserVerificationSessionStatusCell.xib */,
|
||||
);
|
||||
path = SessionsStatus;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1BEE73D23E08AC30003A4CB /* SessionStatus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE74523E093260003A4CB /* UserVerificationSessionStatusCoordinator.swift */,
|
||||
B1BEE74223E093240003A4CB /* UserVerificationSessionStatusCoordinatorType.swift */,
|
||||
B1BEE74323E093250003A4CB /* UserVerificationSessionStatusViewAction.swift */,
|
||||
B1BEE74123E093230003A4CB /* UserVerificationSessionStatusViewController.storyboard */,
|
||||
B1BEE74423E093250003A4CB /* UserVerificationSessionStatusViewController.swift */,
|
||||
B1BEE74023E093230003A4CB /* UserVerificationSessionStatusViewModel.swift */,
|
||||
B1BEE73F23E093230003A4CB /* UserVerificationSessionStatusViewModelType.swift */,
|
||||
B1BEE73E23E093220003A4CB /* UserVerificationSessionStatusViewState.swift */,
|
||||
);
|
||||
path = SessionStatus;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1C3361A22F328AE0021BA8D /* Camera */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4165,6 +4289,7 @@
|
|||
B1A6C113238BD245002882FD /* SlidingModalContainerView.xib in Resources */,
|
||||
B1B5594420EF7BD000210D55 /* TableViewCellWithCollectionView.xib in Resources */,
|
||||
B1B558D520EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B12D79FC23E2462200FACEDC /* UserVerificationStartViewController.storyboard in Resources */,
|
||||
B1B5590020EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B1B5597020EFA85D00210D55 /* EncryptionInfoView.xib in Resources */,
|
||||
B1B558BC20EF768F00210D55 /* RoomMembershipBubbleCell.xib in Resources */,
|
||||
|
@ -4187,6 +4312,7 @@
|
|||
B1B9DEEA22EB34EF0065E677 /* ReactionHistoryViewController.storyboard in Resources */,
|
||||
B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */,
|
||||
B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */,
|
||||
B1BEE73A23DF44A60003A4CB /* UserVerificationSessionsStatusViewController.storyboard in Resources */,
|
||||
B1B5572120EE6C4D00210D55 /* ContactsTableViewController.xib in Resources */,
|
||||
B1B5593A20EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.xib in Resources */,
|
||||
B1B558D820EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
|
@ -4256,10 +4382,12 @@
|
|||
B1B558D120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1B558FE20EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B5581D20EF625800210D55 /* RoomAvatarTitleView.xib in Resources */,
|
||||
B1BEE74923E093260003A4CB /* UserVerificationSessionStatusViewController.storyboard in Resources */,
|
||||
B1B5590B20EF768F00210D55 /* RoomMembershipExpandedBubbleCell.xib in Resources */,
|
||||
B1B558E720EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1B558DC20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B5572E20EE6C4D00210D55 /* ReadReceiptsViewController.xib in Resources */,
|
||||
B1BEE72B23DF38B20003A4CB /* UserVerificationSessionStatusCell.xib in Resources */,
|
||||
B1B5574220EE6C4D00210D55 /* RecentsViewController.xib in Resources */,
|
||||
B1B5571C20EE6C4D00210D55 /* DeactivateAccountViewController.storyboard in Resources */,
|
||||
B1B5596520EF9E9B00210D55 /* RoomTableViewCell.xib in Resources */,
|
||||
|
@ -4549,6 +4677,7 @@
|
|||
3232AB4C2256558300AD6A5C /* TemplateScreenCoordinator.swift in Sources */,
|
||||
B1B5575920EE6C4D00210D55 /* HomeMessagesSearchViewController.m in Sources */,
|
||||
B1B558DE20EF768F00210D55 /* RoomIncomingAttachmentBubbleCell.m in Sources */,
|
||||
B1BEE74D23E093260003A4CB /* UserVerificationSessionStatusCoordinator.swift in Sources */,
|
||||
B1B5574820EE6C4D00210D55 /* PeopleViewController.m in Sources */,
|
||||
B1B5598720EFC3E000210D55 /* Widget.m in Sources */,
|
||||
B1BD71C1238EA92100BA92E2 /* WidgetPermissionViewModel.swift in Sources */,
|
||||
|
@ -4556,6 +4685,7 @@
|
|||
B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */,
|
||||
3232ABAA225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5574420EE6C4D00210D55 /* CallViewController.m in Sources */,
|
||||
B12D7A0023E2462200FACEDC /* UserVerificationStartCoordinatorType.swift in Sources */,
|
||||
B1B5572220EE6C4D00210D55 /* RoomSettingsViewController.m in Sources */,
|
||||
B1B5577320EE702800210D55 /* JitsiViewController.m in Sources */,
|
||||
B169331620F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m in Sources */,
|
||||
|
@ -4564,6 +4694,7 @@
|
|||
32242F1221E8FBA900725742 /* ThemeService.m in Sources */,
|
||||
B1B558E820EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B558F320EF768F00210D55 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B12D79FE23E2462200FACEDC /* UserVerificationStartViewController.swift in Sources */,
|
||||
B1B557BD20EF5B4500210D55 /* KeyboardGrowingTextView.m in Sources */,
|
||||
B1A68593229E807A00D6C09A /* RoomBubbleCellLayout.swift in Sources */,
|
||||
B1B558F420EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
|
@ -4577,6 +4708,7 @@
|
|||
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */,
|
||||
B1DCC61C22E5E17100625807 /* EmojiPickerViewAction.swift in Sources */,
|
||||
B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */,
|
||||
B1BEE71523DF2ACF0003A4CB /* UserVerificationCoordinator.swift in Sources */,
|
||||
B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
3232ABC022594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift in Sources */,
|
||||
B1963B2E228F1C4900CBA17F /* BubbleReactionViewData.swift in Sources */,
|
||||
|
@ -4605,6 +4737,7 @@
|
|||
B1C45A8C232A8C2600165425 /* SettingsIdentityServerViewAction.swift in Sources */,
|
||||
32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */,
|
||||
B1BEE74623E093260003A4CB /* UserVerificationSessionStatusViewState.swift in Sources */,
|
||||
B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */,
|
||||
B1A6C111238BD236002882FD /* SlidingModalContainerView.swift in Sources */,
|
||||
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */,
|
||||
|
@ -4630,6 +4763,7 @@
|
|||
B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */,
|
||||
B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */,
|
||||
B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */,
|
||||
B1BEE74C23E093260003A4CB /* UserVerificationSessionStatusViewController.swift in Sources */,
|
||||
B1B9DEEB22EB34EF0065E677 /* ReactionHistoryViewModel.swift in Sources */,
|
||||
B1C543B023A2871300DCA1FA /* KeyVerificationBaseBubbleCell.swift in Sources */,
|
||||
B1963B2F228F1C4900CBA17F /* BubbleReactionViewCell.swift in Sources */,
|
||||
|
@ -4702,7 +4836,9 @@
|
|||
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */,
|
||||
B157FA9F23264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift in Sources */,
|
||||
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */,
|
||||
B12D7A0123E2462200FACEDC /* UserVerificationStartViewModel.swift in Sources */,
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */,
|
||||
B1BEE73623DF44A60003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift in Sources */,
|
||||
B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */,
|
||||
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
|
||||
B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */,
|
||||
|
@ -4713,8 +4849,10 @@
|
|||
3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */,
|
||||
B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */,
|
||||
B1BEE74B23E093260003A4CB /* UserVerificationSessionStatusViewAction.swift in Sources */,
|
||||
3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */,
|
||||
B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */,
|
||||
B1BEE73923DF44A60003A4CB /* UserVerificationSessionsStatusViewController.swift in Sources */,
|
||||
B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */,
|
||||
B1C562E3228C7C8D0037F12A /* RoomContextualMenuPresenter.swift in Sources */,
|
||||
B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */,
|
||||
|
@ -4722,10 +4860,12 @@
|
|||
B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */,
|
||||
322C110822BBC6F80043FEAC /* WidgetManagerConfig.swift in Sources */,
|
||||
F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */,
|
||||
B1BEE71423DF2ACF0003A4CB /* UserVerificationCoordinatorType.swift in Sources */,
|
||||
B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */,
|
||||
B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */,
|
||||
3232ABBB2257BE6500AD6A5C /* DeviceVerificationVerifyViewState.swift in Sources */,
|
||||
3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */,
|
||||
B1BEE73B23DF44A60003A4CB /* UserVerificationSessionsStatusCoordinator.swift in Sources */,
|
||||
B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */,
|
||||
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */,
|
||||
B1DCC61E22E5E17100625807 /* EmojiPickerViewModel.swift in Sources */,
|
||||
|
@ -4733,6 +4873,7 @@
|
|||
32863A5C2384074C00D07C4A /* RiotSettingAllowedWidgets.swift in Sources */,
|
||||
B1B9DEDA22E9B7350065E677 /* SerializationService.swift in Sources */,
|
||||
B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */,
|
||||
B12D79FD23E2462200FACEDC /* UserVerificationStartViewModelType.swift in Sources */,
|
||||
B1C543AE23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift in Sources */,
|
||||
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */,
|
||||
B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */,
|
||||
|
@ -4744,6 +4885,7 @@
|
|||
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */,
|
||||
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
|
||||
B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */,
|
||||
B1BEE72A23DF38B20003A4CB /* UserVerificationSessionStatusCell.swift in Sources */,
|
||||
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */,
|
||||
B1B5574020EE6C4D00210D55 /* SegmentedViewController.m in Sources */,
|
||||
|
@ -4766,6 +4908,7 @@
|
|||
32A6001D22C661100042C1D9 /* EditHistoryCoordinatorType.swift in Sources */,
|
||||
F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */,
|
||||
B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */,
|
||||
B1BEE73823DF44A60003A4CB /* UserVerificationSessionsStatusViewAction.swift in Sources */,
|
||||
B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */,
|
||||
B1DCC62822E60CE300625807 /* EmojiCategory.swift in Sources */,
|
||||
B14084CC23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift in Sources */,
|
||||
|
@ -4780,6 +4923,7 @@
|
|||
B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */,
|
||||
B1B5577120EE702800210D55 /* StickerPickerViewController.m in Sources */,
|
||||
32FDC1CD2386CD390084717A /* RiotSettingIntegrationProvisioning.swift in Sources */,
|
||||
B1BEE73523DF44A60003A4CB /* UserVerificationSessionsStatusViewModel.swift in Sources */,
|
||||
B104C2942203773C00D9F496 /* KeyBackupBannerPreferences.swift in Sources */,
|
||||
B1B5572020EE6C4D00210D55 /* ContactsTableViewController.m in Sources */,
|
||||
B1B5581920EF625800210D55 /* RoomTitleView.m in Sources */,
|
||||
|
@ -4794,8 +4938,10 @@
|
|||
B1B5597520EFB02A00210D55 /* InviteRecentTableViewCell.m in Sources */,
|
||||
B1B5571E20EE6C4D00210D55 /* ContactDetailsViewController.m in Sources */,
|
||||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */,
|
||||
B1BEE74823E093260003A4CB /* UserVerificationSessionStatusViewModel.swift in Sources */,
|
||||
B1A6C10723881EF2002882FD /* SlidingModalPresenter.swift in Sources */,
|
||||
B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */,
|
||||
B12D7A0223E2462200FACEDC /* UserVerificationStartViewAction.swift in Sources */,
|
||||
B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */,
|
||||
B1DCC61A22E5E17100625807 /* EmojiPickerViewController.swift in Sources */,
|
||||
B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */,
|
||||
|
@ -4808,6 +4954,7 @@
|
|||
329E746722CD02EA006F9797 /* BubbleReactionActionViewCell.swift in Sources */,
|
||||
B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */,
|
||||
B1098BF721ECFE65000DDA48 /* PasswordStrength.swift in Sources */,
|
||||
B1BEE73423DF44A60003A4CB /* UserVerificationSessionsStatusViewModelType.swift in Sources */,
|
||||
324A2052225FC571004FE8B0 /* DeviceVerificationIncomingViewAction.swift in Sources */,
|
||||
B105778D2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift in Sources */,
|
||||
B1B557D820EF5EA900210D55 /* RoomActivitiesView.m in Sources */,
|
||||
|
@ -4824,6 +4971,7 @@
|
|||
B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */,
|
||||
B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */,
|
||||
B1098BE121ECE09F000DDA48 /* Images.swift in Sources */,
|
||||
B1BEE74A23E093260003A4CB /* UserVerificationSessionStatusCoordinatorType.swift in Sources */,
|
||||
3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */,
|
||||
B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */,
|
||||
3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -4837,6 +4985,7 @@
|
|||
B125FE23231D5E4300B72806 /* SettingsDiscoveryViewState.swift in Sources */,
|
||||
B1B5593820EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.m in Sources */,
|
||||
B1DCC62222E60BE000625807 /* EmojiPickerItemViewData.swift in Sources */,
|
||||
B1BEE74723E093260003A4CB /* UserVerificationSessionStatusViewModelType.swift in Sources */,
|
||||
3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */,
|
||||
B1C335CD22F1C1320021BA8D /* CameraPresenter.swift in Sources */,
|
||||
B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */,
|
||||
|
@ -4873,6 +5022,7 @@
|
|||
B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */,
|
||||
B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */,
|
||||
B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */,
|
||||
B12D79FB23E2462200FACEDC /* UserVerificationStartCoordinator.swift in Sources */,
|
||||
B169330320F3C98900746532 /* RoomBubbleCellData.m in Sources */,
|
||||
B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */,
|
||||
B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */,
|
||||
|
@ -4911,6 +5061,7 @@
|
|||
3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */,
|
||||
B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */,
|
||||
B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */,
|
||||
B1BEE71623DF2ACF0003A4CB /* UserVerificationCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */,
|
||||
B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */,
|
||||
|
@ -4920,10 +5071,12 @@
|
|||
B125FE1B231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift in Sources */,
|
||||
32242F0921E8B05F00725742 /* UIColor.swift in Sources */,
|
||||
B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */,
|
||||
B12D79FF23E2462200FACEDC /* UserVerificationStartViewState.swift in Sources */,
|
||||
B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */,
|
||||
B1B5577D20EE84BF00210D55 /* CircleButton.m in Sources */,
|
||||
32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */,
|
||||
B109D6F1222D8C400061B6D9 /* UIApplication.swift in Sources */,
|
||||
B1BEE73723DF44A60003A4CB /* UserVerificationSessionsStatusViewState.swift in Sources */,
|
||||
B108932823ABEE6800802670 /* BubbleCellReadReceiptsDisplayable.swift in Sources */,
|
||||
B1B558FF20EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.m in Sources */,
|
||||
B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */,
|
||||
|
@ -4932,6 +5085,7 @@
|
|||
B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */,
|
||||
B1C543A4239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift in Sources */,
|
||||
32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */,
|
||||
B12D7A0423E43DCC00FACEDC /* KeyVerificationKind.swift in Sources */,
|
||||
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */,
|
||||
B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */,
|
||||
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */,
|
||||
|
|
|
@ -140,6 +140,8 @@ extern NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey;
|
|||
- (BOOL)presentIncomingKeyVerificationRequest:(MXKeyVerificationRequest*)incomingKeyVerificationRequest
|
||||
inSession:(MXSession*)session;
|
||||
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession;
|
||||
|
||||
#pragma mark - Matrix Accounts handling
|
||||
|
||||
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection;
|
||||
|
|
|
@ -4890,6 +4890,29 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
return presented;
|
||||
}
|
||||
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession
|
||||
{
|
||||
NSLog(@"[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: %@", roomMember);
|
||||
|
||||
BOOL presented = NO;
|
||||
if (!deviceVerificationCoordinatorBridgePresenter)
|
||||
{
|
||||
UIViewController *presentingViewController = self.window.rootViewController.presentedViewController ?: self.window.rootViewController;
|
||||
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:mxSession];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:presentingViewController roomMember:roomMember animated:YES];
|
||||
|
||||
presented = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: Controller already presented.");
|
||||
}
|
||||
return presented;
|
||||
}
|
||||
|
||||
- (void)deviceVerificationCoordinatorBridgePresenterDelegateDidComplete:(DeviceVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
[deviceVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
|
|
|
@ -117,6 +117,21 @@ internal enum StoryboardScene {
|
|||
|
||||
internal static let initialScene = InitialSceneType<Riot.TemplateScreenViewController>(storyboard: TemplateScreenViewController.self)
|
||||
}
|
||||
internal enum UserVerificationSessionStatusViewController: StoryboardType {
|
||||
internal static let storyboardName = "UserVerificationSessionStatusViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.UserVerificationSessionStatusViewController>(storyboard: UserVerificationSessionStatusViewController.self)
|
||||
}
|
||||
internal enum UserVerificationSessionsStatusViewController: StoryboardType {
|
||||
internal static let storyboardName = "UserVerificationSessionsStatusViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.UserVerificationSessionsStatusViewController>(storyboard: UserVerificationSessionsStatusViewController.self)
|
||||
}
|
||||
internal enum UserVerificationStartViewController: StoryboardType {
|
||||
internal static let storyboardName = "UserVerificationStartViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.UserVerificationStartViewController>(storyboard: UserVerificationStartViewController.self)
|
||||
}
|
||||
internal enum WidgetPermissionViewController: StoryboardType {
|
||||
internal static let storyboardName = "WidgetPermissionViewController"
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -57,14 +55,15 @@
|
|||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="leading" secondItem="FfX-ul-Kr4" secondAttribute="trailing" constant="14" id="A6H-TC-2Pg"/>
|
||||
<constraint firstAttribute="bottom" secondItem="FfX-ul-Kr4" secondAttribute="bottom" constant="15.5" id="D2o-qq-OsZ"/>
|
||||
<constraint firstItem="O8i-B6-S6A" firstAttribute="centerY" secondItem="FfX-ul-Kr4" secondAttribute="centerY" id="L8U-Xi-rD7"/>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="centerY" secondItem="aXz-IR-jj5" secondAttribute="centerY" id="O6E-Di-2d4"/>
|
||||
<constraint firstItem="apY-Nk-wQh" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="topMargin" constant="34" id="Rlm-bQ-Qpr"/>
|
||||
<constraint firstItem="O8i-B6-S6A" firstAttribute="width" secondItem="FfX-ul-Kr4" secondAttribute="width" id="Sej-VT-sBx"/>
|
||||
<constraint firstAttribute="trailingMargin" relation="greaterThanOrEqual" secondItem="Lg1-xQ-AGn" secondAttribute="trailing" constant="15" id="U1F-vo-7f6"/>
|
||||
<constraint firstItem="apY-Nk-wQh" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leadingMargin" constant="42" id="dut-Df-DIU"/>
|
||||
<constraint firstItem="FfX-ul-Kr4" firstAttribute="centerY" secondItem="aXz-IR-jj5" secondAttribute="centerY" id="gUw-GV-DPX"/>
|
||||
<constraint firstItem="FfX-ul-Kr4" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leadingMargin" constant="6" id="qey-6T-URF"/>
|
||||
<constraint firstItem="FfX-ul-Kr4" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="16" id="wyT-JI-kQS"/>
|
||||
<constraint firstItem="O8i-B6-S6A" firstAttribute="centerX" secondItem="FfX-ul-Kr4" secondAttribute="centerX" id="xfK-sI-YJQ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
|
|
|
@ -28,10 +28,13 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
private let navigationRouter: NavigationRouterType
|
||||
private let session: MXSession
|
||||
private let otherUserId: String
|
||||
private let otherDeviceId: String
|
||||
|
||||
private let otherDeviceId: String
|
||||
|
||||
private var incomingTransaction: MXIncomingSASTransaction?
|
||||
private var incomingKeyVerificationRequest: MXKeyVerificationRequest?
|
||||
|
||||
private var verificationKind: KeyVerificationKind = .device
|
||||
private var roomMember: MXRoomMember?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -54,6 +57,13 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
self.otherUserId = otherUserId
|
||||
self.otherDeviceId = otherDeviceId
|
||||
}
|
||||
|
||||
init(navigationRouter: NavigationRouterType, session: MXSession, userId: String, otherDeviceId: String) {
|
||||
self.navigationRouter = navigationRouter
|
||||
self.session = session
|
||||
self.otherUserId = userId
|
||||
self.otherDeviceId = otherDeviceId
|
||||
}
|
||||
|
||||
/// Contrustor to manage an incoming SAS device verification transaction
|
||||
///
|
||||
|
@ -77,13 +87,29 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
self.incomingKeyVerificationRequest = incomingKeyVerificationRequest
|
||||
}
|
||||
|
||||
/// Constructor to start a user verification.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - session: the MXSession
|
||||
/// - roomMember: an other room member
|
||||
init(session: MXSession, roomMember: MXRoomMember) {
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.session = session
|
||||
self.otherUserId = roomMember.userId
|
||||
self.otherDeviceId = ""
|
||||
self.roomMember = roomMember
|
||||
self.verificationKind = .user
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let rootCoordinator: Coordinator & Presentable
|
||||
|
||||
let rootCoordinator: Coordinator & Presentable
|
||||
|
||||
if let incomingKeyVerificationRequest = self.incomingKeyVerificationRequest {
|
||||
rootCoordinator = self.createDataLoadingScreenCoordinator(with: incomingKeyVerificationRequest)
|
||||
} else if let roomMember = self.roomMember {
|
||||
rootCoordinator = self.createUserVerificationStartCoordinator(with: roomMember)
|
||||
} else {
|
||||
rootCoordinator = self.createDataLoadingScreenCoordinator()
|
||||
}
|
||||
|
@ -91,8 +117,15 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
|
||||
if self.navigationRouter.modules.isEmpty == false {
|
||||
self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
})
|
||||
} else {
|
||||
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,6 +150,14 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func createUserVerificationStartCoordinator(with roomMember: MXRoomMember) -> UserVerificationStartCoordinator {
|
||||
let coordinator = UserVerificationStartCoordinator(session: self.session, roomMember: roomMember)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func showStart(otherUser: MXUser, otherDevice: MXDeviceInfo) {
|
||||
let coordinator = DeviceVerificationStartCoordinator(session: self.session, otherUser: otherUser, otherDevice: otherDevice)
|
||||
|
@ -141,7 +182,7 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
}
|
||||
|
||||
private func showVerify(transaction: MXSASTransaction, animated: Bool) {
|
||||
let coordinator = DeviceVerificationVerifyCoordinator(session: self.session, transaction: transaction)
|
||||
let coordinator = DeviceVerificationVerifyCoordinator(session: self.session, transaction: transaction, verificationKind: self.verificationKind)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
|
@ -152,7 +193,7 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
}
|
||||
|
||||
private func showVerified(animated: Bool) {
|
||||
let viewController = DeviceVerificationVerifiedViewController.instantiate()
|
||||
let viewController = DeviceVerificationVerifiedViewController.instantiate(with: self.verificationKind)
|
||||
viewController.delegate = self
|
||||
self.navigationRouter.setRootModule(viewController)
|
||||
}
|
||||
|
@ -226,3 +267,17 @@ extension DeviceVerificationCoordinator: DeviceVerificationVerifiedViewControlle
|
|||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: UserVerificationStartCoordinatorDelegate {
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
||||
self.showVerify(transaction: transaction, animated: true)
|
||||
}
|
||||
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
|
||||
func userVerificationStartCoordinatorDidCancel(_ coordinator: UserVerificationStartCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,18 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
|
||||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func present(from viewController: UIViewController, roomMember: MXRoomMember, animated: Bool) {
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Present from \(viewController)")
|
||||
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, roomMember: roomMember)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func present(from viewController: UIViewController, incomingTransaction: MXIncomingSASTransaction, animated: Bool) {
|
||||
|
||||
|
|
21
Riot/Modules/DeviceVerification/KeyVerificationKind.swift
Normal file
21
Riot/Modules/DeviceVerification/KeyVerificationKind.swift
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
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 KeyVerificationKind {
|
||||
case device
|
||||
case user
|
||||
}
|
|
@ -66,6 +66,13 @@ final class DeviceVerificationDataLoadingViewController: UIViewController {
|
|||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Hide back button
|
||||
self.navigationItem.setHidesBackButton(true, animated: animated)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
|
|
|
@ -39,6 +39,7 @@ final class DeviceVerificationVerifiedViewController: UIViewController {
|
|||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var verificationKind: KeyVerificationKind = .user
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -46,9 +47,10 @@ final class DeviceVerificationVerifiedViewController: UIViewController {
|
|||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate() -> DeviceVerificationVerifiedViewController {
|
||||
class func instantiate(with verificationKind: KeyVerificationKind) -> DeviceVerificationVerifiedViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationVerifiedViewController.initialScene.instantiate()
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
viewController.verificationKind = verificationKind
|
||||
return viewController
|
||||
}
|
||||
|
||||
|
@ -59,7 +61,6 @@ final class DeviceVerificationVerifiedViewController: UIViewController {
|
|||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
|
@ -81,9 +82,28 @@ final class DeviceVerificationVerifiedViewController: UIViewController {
|
|||
// MARK: - Private
|
||||
|
||||
private func setupViews() {
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifiedTitle
|
||||
self.description1Label.text = VectorL10n.deviceVerificationVerifiedDescription1
|
||||
self.description2Label.text = VectorL10n.deviceVerificationVerifiedDescription2
|
||||
let title: String
|
||||
let bodyTitle: String
|
||||
let descriptionTextPart1: String
|
||||
let descriptionTextPart2: String
|
||||
|
||||
switch self.verificationKind {
|
||||
case .device:
|
||||
title = VectorL10n.deviceVerificationTitle
|
||||
bodyTitle = VectorL10n.deviceVerificationVerifiedTitle
|
||||
descriptionTextPart1 = VectorL10n.deviceVerificationVerifiedDescription1
|
||||
descriptionTextPart2 = VectorL10n.deviceVerificationVerifiedDescription2
|
||||
case .user:
|
||||
title = "Verify user"
|
||||
bodyTitle = VectorL10n.deviceVerificationVerifiedTitle
|
||||
descriptionTextPart1 = "You’ve successfully verified this user."
|
||||
descriptionTextPart2 = "Messages with this user in this room are end-to-end encrypted and can’t be read by third parties."
|
||||
}
|
||||
|
||||
self.title = title
|
||||
self.titleLabel.text = bodyTitle
|
||||
self.description1Label.text = descriptionTextPart1
|
||||
self.description2Label.text = descriptionTextPart2
|
||||
|
||||
self.okButton.setTitle(VectorL10n.deviceVerificationVerifiedGotItButton, for: .normal)
|
||||
}
|
||||
|
@ -103,7 +123,7 @@ final class DeviceVerificationVerifiedViewController: UIViewController {
|
|||
|
||||
self.okButtonBackgroundView.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.okButton)
|
||||
}
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
|
|
|
@ -38,10 +38,10 @@ final class DeviceVerificationVerifyCoordinator: DeviceVerificationVerifyCoordin
|
|||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, transaction: MXSASTransaction) {
|
||||
init(session: MXSession, transaction: MXSASTransaction, verificationKind: KeyVerificationKind) {
|
||||
self.session = session
|
||||
|
||||
let deviceVerificationVerifyViewModel = DeviceVerificationVerifyViewModel(session: self.session, transaction: transaction)
|
||||
let deviceVerificationVerifyViewModel = DeviceVerificationVerifyViewModel(session: self.session, transaction: transaction, verificationKind: verificationKind)
|
||||
let deviceVerificationVerifyViewController = DeviceVerificationVerifyViewController.instantiate(with: deviceVerificationVerifyViewModel)
|
||||
self.deviceVerificationVerifyViewModel = deviceVerificationVerifyViewModel
|
||||
self.deviceVerificationVerifyViewController = deviceVerificationVerifyViewController
|
||||
|
|
|
@ -59,7 +59,6 @@ final class DeviceVerificationVerifyViewController: UIViewController {
|
|||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
|
@ -123,16 +122,33 @@ final class DeviceVerificationVerifyViewController: UIViewController {
|
|||
|
||||
self.scrollView.keyboardDismissMode = .interactive
|
||||
|
||||
if viewModel.emojis != nil {
|
||||
let isVerificationByEmoji = viewModel.emojis != nil
|
||||
|
||||
if isVerificationByEmoji {
|
||||
self.decimalLabel.isHidden = true
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifyTitleEmoji
|
||||
} else {
|
||||
self.emojisCollectionView.isHidden = true
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifyTitleNumber
|
||||
self.decimalLabel.text = self.viewModel.decimal
|
||||
}
|
||||
|
||||
let title: String
|
||||
let instructionText: String
|
||||
let adviceText: String
|
||||
|
||||
switch viewModel.verificationKind {
|
||||
case .device:
|
||||
title = VectorL10n.deviceVerificationTitle
|
||||
instructionText = isVerificationByEmoji ? VectorL10n.deviceVerificationVerifyTitleEmoji : VectorL10n.deviceVerificationVerifyTitleNumber
|
||||
adviceText = VectorL10n.deviceVerificationSecurityAdvice
|
||||
case .user:
|
||||
title = "Verify user"
|
||||
instructionText = isVerificationByEmoji ? "Verify this user by confirming the following unique emoji appears on their screen, in the same order." : "Verify this user by confirming the following numbers appear on their screen, in the same order."
|
||||
adviceText = VectorL10n.deviceVerificationSecurityAdvice
|
||||
}
|
||||
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice
|
||||
self.title = title
|
||||
self.titleLabel.text = instructionText
|
||||
self.informationLabel.text = adviceText
|
||||
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner
|
||||
|
||||
self.waitingPartnerLabel.isHidden = true
|
||||
|
@ -223,10 +239,8 @@ extension DeviceVerificationVerifyViewController: UICollectionViewDataSource {
|
|||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
|
||||
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VerifyEmojiCollectionViewCell", for: indexPath) as? VerifyEmojiCollectionViewCell else {
|
||||
return UICollectionViewCell()
|
||||
}
|
||||
|
||||
let cell = collectionView.dequeueReusableCell(for: indexPath, cellType: VerifyEmojiCollectionViewCell.self)
|
||||
|
||||
guard let emoji = self.viewModel.emojis?[indexPath.row] else {
|
||||
return UICollectionViewCell()
|
||||
|
|
|
@ -31,16 +31,19 @@ final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModel
|
|||
|
||||
weak var viewDelegate: DeviceVerificationVerifyViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: DeviceVerificationVerifyViewModelCoordinatorDelegate?
|
||||
var emojis: [MXEmojiRepresentation]?
|
||||
var decimal: String?
|
||||
|
||||
let emojis: [MXEmojiRepresentation]?
|
||||
let decimal: String?
|
||||
let verificationKind: KeyVerificationKind
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, transaction: MXSASTransaction) {
|
||||
init(session: MXSession, transaction: MXSASTransaction, verificationKind: KeyVerificationKind) {
|
||||
self.session = session
|
||||
self.transaction = transaction
|
||||
self.emojis = self.transaction.sasEmoji
|
||||
self.decimal = self.transaction.sasDecimal
|
||||
self.verificationKind = verificationKind
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
|
@ -35,6 +35,7 @@ protocol DeviceVerificationVerifyViewModelType {
|
|||
|
||||
func process(viewAction: DeviceVerificationVerifyViewAction)
|
||||
|
||||
var emojis: [MXEmojiRepresentation]? { get set }
|
||||
var decimal: String? { get set }
|
||||
var emojis: [MXEmojiRepresentation]? { get }
|
||||
var decimal: String? { get }
|
||||
var verificationKind: KeyVerificationKind { get }
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
class VerifyEmojiCollectionViewCell: UICollectionViewCell, Themable {
|
||||
class VerifyEmojiCollectionViewCell: UICollectionViewCell, Reusable, Themable {
|
||||
@IBOutlet weak var emoji: UILabel!
|
||||
@IBOutlet weak var name: UILabel!
|
||||
|
||||
|
|
|
@ -18,18 +18,6 @@
|
|||
|
||||
#import "DeviceTableViewCell.h"
|
||||
|
||||
@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController <UIGestureRecognizerDelegate, DeviceTableViewCellDelegate>
|
||||
@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarHeaderBackground;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomMemberAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *memberHeaderView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberNameLabelMask;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberStatusLabel;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomImageView;
|
||||
@end
|
||||
|
||||
|
|
|
@ -33,10 +33,13 @@
|
|||
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
|
||||
#define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f
|
||||
|
||||
@interface RoomMemberDetailsViewController () <RoomMemberTitleViewDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
@interface RoomMemberDetailsViewController () <UIGestureRecognizerDelegate, DeviceTableViewCellDelegate, RoomMemberTitleViewDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
RoomMemberTitleView* memberTitleView;
|
||||
|
||||
NSInteger securityIndex;
|
||||
NSMutableArray<NSNumber*> *securityActionsArray;
|
||||
|
||||
/**
|
||||
List of the admin actions on this member.
|
||||
*/
|
||||
|
@ -78,6 +81,26 @@
|
|||
*/
|
||||
BOOL isStatusBarHidden;
|
||||
}
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarHeaderBackground;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomMemberAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *memberHeaderView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *roomMemberAvatarBadgeImageView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberNameLabelMask;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberStatusLabel;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomImageView;
|
||||
|
||||
|
||||
@property(nonatomic) UserEncryptionTrustLevel encryptionTrustLevel;
|
||||
|
||||
@property(nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomMemberDetailsViewController
|
||||
|
@ -105,6 +128,7 @@
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
self.encryptionTrustLevel = UserEncryptionTrustLevelUnknown;
|
||||
|
||||
adminActionsArray = [[NSMutableArray alloc] init];
|
||||
otherActionsArray = [[NSMutableArray alloc] init];
|
||||
|
@ -195,10 +219,15 @@
|
|||
[self.tableView registerClass:TableViewCellWithButton.class forCellReuseIdentifier:[TableViewCellWithButton defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:RoomTableViewCell.class forCellReuseIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:DeviceTableViewCell.class forCellReuseIdentifier:[DeviceTableViewCell defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:MXKTableViewCell.class forCellReuseIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
|
||||
|
||||
// Hide line separators of empty cells
|
||||
self.tableView.tableFooterView = [[UIView alloc] init];
|
||||
|
||||
// Enable self sizing cells
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 50;
|
||||
|
||||
// Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
|
||||
UIApplicationWillChangeStatusBarOrientationNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
@ -262,6 +291,8 @@
|
|||
// Handle here the bottom image visibility
|
||||
UIInterfaceOrientation screenOrientation = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
self.bottomImageView.hidden = (screenOrientation == UIInterfaceOrientationLandscapeLeft || screenOrientation == UIInterfaceOrientationLandscapeRight);
|
||||
|
||||
[self refreshUserEncryptionTrustLevel];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -382,14 +413,18 @@
|
|||
|
||||
NSString* presenceText;
|
||||
|
||||
if (self.mxRoomMember.userId)
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
|
||||
if (userId)
|
||||
{
|
||||
MXUser *user = [self.mxRoom.mxSession userWithUserId:self.mxRoomMember.userId];
|
||||
MXUser *user = [self.mxRoom.mxSession userWithUserId:userId];
|
||||
presenceText = [Tools presenceText:user];
|
||||
}
|
||||
|
||||
self.roomMemberStatusLabel.text = presenceText;
|
||||
|
||||
self.roomMemberAvatarBadgeImageView.image = self.userEncryptionBadgeImage;
|
||||
|
||||
// Retrieve the existing direct chats
|
||||
[directChatsArray removeAllObjects];
|
||||
NSArray *directRoomIds = self.mainSession.directRooms[self.mxRoomMember.userId];
|
||||
|
@ -403,39 +438,130 @@
|
|||
}
|
||||
|
||||
// Retrieve member's devices
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
[self.mxRoom.mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
self->devicesArray = usersDevicesInfoMap.map[userId].allValues;
|
||||
// Reload the full table to take into account a potential change on a device status.
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomMemberDetailsVC] Crypto failed to download device info for user: %@", userId);
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
// Notify the end user
|
||||
NSString *myUserId = self.mainSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
}
|
||||
|
||||
}];
|
||||
|
||||
if (!RiotSettings.shared.enableCrossSigning)
|
||||
{
|
||||
[self.mxRoom.mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
self->devicesArray = usersDevicesInfoMap.map[userId].allValues;
|
||||
// Reload the full table to take into account a potential change on a device status.
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomMemberDetailsVC] Crypto failed to download device info for user: %@", userId);
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
// Notify the end user
|
||||
NSString *myUserId = self.mainSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Complete data update and reload table view
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
- (void)refreshUserEncryptionTrustLevel
|
||||
{
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
|
||||
if (!userId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.mxRoom.summary.isEncrypted && self.mxRoom.mxSession.crypto)
|
||||
{
|
||||
MXUsersTrustLevelSummary *usersTrustLevelSummary = [self.mxRoom.mxSession.crypto trustLevelSummaryForUserIds:@[userId]];
|
||||
|
||||
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
||||
|
||||
if (trustedDevicesPercentage >= 1.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelTrusted;
|
||||
}
|
||||
else if (trustedDevicesPercentage == 0.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelWarning;
|
||||
}
|
||||
|
||||
self.encryptionTrustLevel = userEncryptionTrustLevel;
|
||||
[self updateMemberInfo];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.encryptionTrustLevel = UserEncryptionTrustLevelNone;
|
||||
[self updateMemberInfo];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage*)userEncryptionBadgeImage
|
||||
{
|
||||
NSString *encryptionIconName;
|
||||
UIImage *encryptionIcon;
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel = self.encryptionTrustLevel;
|
||||
|
||||
switch (userEncryptionTrustLevel) {
|
||||
case RoomEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
encryptionIcon = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
|
||||
return encryptionIcon;
|
||||
}
|
||||
|
||||
- (BOOL)isRoomMemberCurrentUser
|
||||
{
|
||||
return [self.mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId];
|
||||
}
|
||||
|
||||
- (void)startUserVerification
|
||||
{
|
||||
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession];
|
||||
}
|
||||
|
||||
- (void)presentUserVerification
|
||||
{
|
||||
UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter = [[UserVerificationCoordinatorBridgePresenter alloc] initWithPresenter:self
|
||||
session:self.mxRoom.mxSession
|
||||
userId:self.mxRoomMember.userId
|
||||
userDisplayName:self.mxRoomMember.displayname];
|
||||
[userVerificationCoordinatorBridgePresenter start];
|
||||
self.userVerificationCoordinatorBridgePresenter = userVerificationCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - Hide/Show navigation bar border
|
||||
|
||||
- (void)hideNavigationBarBorder:(BOOL)isHidden
|
||||
|
@ -484,7 +610,7 @@
|
|||
[otherActionsArray removeAllObjects];
|
||||
|
||||
// Consider the case of the user himself
|
||||
if ([self.mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId])
|
||||
if (self.isRoomMemberCurrentUser)
|
||||
{
|
||||
isOneself = YES;
|
||||
|
||||
|
@ -618,7 +744,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
|
||||
if (RiotSettings.shared.enableCrossSigning)
|
||||
{
|
||||
securityIndex = sectionCount++;
|
||||
}
|
||||
|
||||
if (otherActionsArray.count)
|
||||
{
|
||||
|
@ -644,7 +775,23 @@
|
|||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
NSInteger numberOfRows;
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
case UserEncryptionTrustLevelNone:
|
||||
numberOfRows = 1;
|
||||
break;
|
||||
default:
|
||||
numberOfRows = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return numberOfRows;
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return adminActionsArray.count;
|
||||
}
|
||||
|
@ -666,10 +813,18 @@
|
|||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
return @"SECURITY";
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_admin_tools", @"Vector", nil);
|
||||
}
|
||||
else if (RiotSettings.shared.enableCrossSigning && section == otherActionsIndex)
|
||||
{
|
||||
return @"OPTIONS";
|
||||
}
|
||||
else if (section == directChatsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_direct_chats", @"Vector", nil);
|
||||
|
@ -741,7 +896,78 @@
|
|||
{
|
||||
UITableViewCell *cell;
|
||||
|
||||
if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
if (indexPath.section == securityIndex)
|
||||
{
|
||||
if (indexPath.row == [self tableView:self.tableView numberOfRowsInSection:indexPath.section] - 1)
|
||||
{
|
||||
MXKTableViewCell *encryptionInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSMutableString *encryptionInformation = [NSMutableString new];
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
[encryptionInformation appendString:@"Loading"];
|
||||
break;
|
||||
case UserEncryptionTrustLevelNone:
|
||||
[encryptionInformation appendString:@"Messages in this room are not end-to-end encrypted."];
|
||||
break;
|
||||
default:
|
||||
[encryptionInformation appendString:@"Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them."];
|
||||
break;
|
||||
}
|
||||
|
||||
[encryptionInformation appendString:@"\n"];
|
||||
|
||||
encryptionInfoCell.textLabel.backgroundColor = [UIColor clearColor];
|
||||
encryptionInfoCell.textLabel.numberOfLines = 0;
|
||||
encryptionInfoCell.textLabel.text = encryptionInformation;
|
||||
encryptionInfoCell.textLabel.font = [UIFont systemFontOfSize:14.0];
|
||||
encryptionInfoCell.textLabel.textColor = ThemeService.shared.theme.headerTextPrimaryColor;
|
||||
|
||||
encryptionInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
encryptionInfoCell.accessoryType = UITableViewCellAccessoryNone;
|
||||
encryptionInfoCell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
encryptionInfoCell.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
|
||||
cell = encryptionInfoCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXKTableViewCell *securityStatusCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSString *statusText;
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
statusText = @"Verified";
|
||||
break;
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
statusText = @"Verify";
|
||||
break;
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
statusText = @"Warning";
|
||||
break;
|
||||
default:
|
||||
statusText = @"Loading";
|
||||
break;
|
||||
}
|
||||
|
||||
securityStatusCell.imageView.image = self.userEncryptionBadgeImage;
|
||||
|
||||
securityStatusCell.textLabel.numberOfLines = 1;
|
||||
securityStatusCell.textLabel.font = [UIFont systemFontOfSize:16.0];
|
||||
securityStatusCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
securityStatusCell.textLabel.text = statusText;
|
||||
|
||||
securityStatusCell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
securityStatusCell.contentView.backgroundColor = [UIColor clearColor];
|
||||
securityStatusCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
securityStatusCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
cell = securityStatusCell;
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
{
|
||||
TableViewCellWithButton *cellWithButton = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
|
@ -852,26 +1078,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == directChatsIndex)
|
||||
{
|
||||
return [RoomTableViewCell cellHeight];
|
||||
}
|
||||
else if (indexPath.section == devicesIndex)
|
||||
{
|
||||
if (indexPath.row < devicesArray.count)
|
||||
{
|
||||
return [DeviceTableViewCell cellHeightWithDeviceInfo:devicesArray[indexPath.row] andCellWidth:self.tableView.frame.size.width];
|
||||
}
|
||||
}
|
||||
|
||||
return TABLEVIEW_ROW_CELL_HEIGHT;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == otherActionsIndex)
|
||||
if (!RiotSettings.shared.enableCrossSigning && section == otherActionsIndex)
|
||||
{
|
||||
return TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN;
|
||||
}
|
||||
|
@ -881,7 +1090,18 @@
|
|||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == directChatsIndex)
|
||||
if (indexPath.section == securityIndex)
|
||||
{
|
||||
if (self.encryptionTrustLevel == UserEncryptionTrustLevelNormal)
|
||||
{
|
||||
[self startUserVerification];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self presentUserVerification];
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == directChatsIndex)
|
||||
{
|
||||
if (indexPath.row < directChatsArray.count)
|
||||
{
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -15,6 +14,7 @@
|
|||
<outlet property="bottomImageView" destination="7Dc-jk-9sT" id="BVN-bt-VXI"/>
|
||||
<outlet property="memberHeaderView" destination="YXr-As-Mqh" id="Eqb-qr-iAo"/>
|
||||
<outlet property="memberThumbnail" destination="GQ1-rP-ckr" id="abr-hr-C3p"/>
|
||||
<outlet property="roomMemberAvatarBadgeImageView" destination="jHh-A3-In3" id="LKN-mv-WFg"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackground" destination="ouj-VM-zdT" id="YeD-zt-8y5"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackgroundHeightConstraint" destination="dBL-G6-Yec" id="QXZ-ZP-0Rn"/>
|
||||
<outlet property="roomMemberAvatarMask" destination="MAS-3M-3cg" id="nLI-7d-5Hu"/>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<rect key="frame" x="137.5" y="0.0" width="100" height="125"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GQ1-rP-ckr" customClass="MXKImageView">
|
||||
<rect key="frame" x="7.5" y="31" width="84" height="84"/>
|
||||
<rect key="frame" x="8" y="31" width="84" height="84"/>
|
||||
<color key="backgroundColor" red="0.6886889638" green="1" blue="0.74383144840000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MemberAvatar"/>
|
||||
<constraints>
|
||||
|
@ -53,11 +53,21 @@
|
|||
<constraint firstAttribute="width" secondItem="GQ1-rP-ckr" secondAttribute="height" multiplier="1:1" id="a1T-Y0-Iic"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="jHh-A3-In3">
|
||||
<rect key="frame" x="68" y="91" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="jHh-A3-In3" secondAttribute="height" multiplier="1:1" id="fvP-Hk-apc"/>
|
||||
<constraint firstAttribute="width" constant="24" id="gBW-ym-4Qv"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomMemberDetailsVCAvatarMask"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="GQ1-rP-ckr" secondAttribute="bottom" constant="10" id="3pC-So-WvO"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="bottom" secondItem="GQ1-rP-ckr" secondAttribute="bottom" id="6Gg-lp-pJw"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="trailing" secondItem="GQ1-rP-ckr" secondAttribute="trailing" id="TbA-vY-3Ef"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="width" secondItem="jHh-A3-In3" secondAttribute="height" multiplier="1:1" id="Ua2-xg-Vd2"/>
|
||||
<constraint firstItem="GQ1-rP-ckr" firstAttribute="centerX" secondItem="MAS-3M-3cg" secondAttribute="centerX" id="ZGI-nR-gGx"/>
|
||||
<constraint firstAttribute="width" constant="100" id="fwv-qE-IV1"/>
|
||||
</constraints>
|
||||
|
|
28
Riot/Modules/Room/Members/Detail/UserEncryptionTrustLevel.h
Normal file
28
Riot/Modules/Room/Members/Detail/UserEncryptionTrustLevel.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
UserEncryptionTrustLevel represents the user trust level in an encrypted room.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, UserEncryptionTrustLevel) {
|
||||
UserEncryptionTrustLevelTrusted,
|
||||
UserEncryptionTrustLevelWarning,
|
||||
UserEncryptionTrustLevelNormal,
|
||||
UserEncryptionTrustLevelNone,
|
||||
UserEncryptionTrustLevelUnknown
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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 UserVerificationSessionStatusCoordinator: UserVerificationSessionStatusCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let deviceId: String
|
||||
private var userVerificationSessionStatusViewModel: UserVerificationSessionStatusViewModelType
|
||||
private let userVerificationSessionStatusViewController: UserVerificationSessionStatusViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationSessionStatusCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.deviceId = deviceId
|
||||
|
||||
let userVerificationSessionStatusViewModel = UserVerificationSessionStatusViewModel(session: self.session, userId: userId, userDisplayName: userDisplayName, deviceId: deviceId)
|
||||
let userVerificationSessionStatusViewController = UserVerificationSessionStatusViewController.instantiate(with: userVerificationSessionStatusViewModel)
|
||||
self.userVerificationSessionStatusViewModel = userVerificationSessionStatusViewModel
|
||||
self.userVerificationSessionStatusViewController = userVerificationSessionStatusViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationSessionStatusViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationSessionStatusViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionStatusViewModelCoordinatorDelegate
|
||||
extension UserVerificationSessionStatusCoordinator: UserVerificationSessionStatusViewModelCoordinatorDelegate {
|
||||
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) {
|
||||
self.delegate?.userVerificationSessionStatusCoordinator(self, wantsToManuallyVerifyDeviceWithId: deviceId, for: userId)
|
||||
}
|
||||
|
||||
func userVerificationSessionStatusViewModelDidClose(_ viewModel: UserVerificationSessionStatusViewModelType) {
|
||||
self.delegate?.userVerificationSessionStatusCoordinatorDidClose(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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 UserVerificationSessionStatusCoordinatorDelegate: class {
|
||||
func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionStatusCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationSessionStatusCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationSessionStatusCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationSessionStatusCoordinatorDelegate? { get }
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationSessionStatusViewController view actions exposed to view model
|
||||
enum UserVerificationSessionStatusViewAction {
|
||||
case loadData
|
||||
case verify
|
||||
case close
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="edG-Ef-G5W">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Session Status View Controller-->
|
||||
<scene sceneID="yMy-34-3pS">
|
||||
<objects>
|
||||
<viewController id="edG-Ef-G5W" customClass="UserVerificationSessionStatusViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="G1A-RF-WrC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="16P-bA-3wP">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sX2-Xu-9f5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="296.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lx-d3-6c2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="108"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qdf-kG-wgx">
|
||||
<rect key="frame" x="325" y="20" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="Pon-L2-rt1"/>
|
||||
<constraint firstAttribute="width" constant="30" id="qav-Na-EDh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" image="close_button"/>
|
||||
<connections>
|
||||
<action selector="closeButtonAction:" destination="edG-Ef-G5W" eventType="touchUpInside" id="0qe-Df-Nw1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="0d0-9z-5Ar">
|
||||
<rect key="frame" x="20" y="23" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="0d0-9z-5Ar" secondAttribute="height" multiplier="1:1" id="EYK-FL-Zx6"/>
|
||||
<constraint firstAttribute="height" constant="24" id="MuJ-Ex-1Vk"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Warning" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mXI-Sg-wYe">
|
||||
<rect key="frame" x="54" y="23" width="261" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Messages with this user in this room are end-to-end encrypted and can’t be read by third parties." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CTY-XM-iLf">
|
||||
<rect key="frame" x="20" y="62" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Qdf-kG-wgx" secondAttribute="trailing" constant="20" id="2JS-ui-NNI"/>
|
||||
<constraint firstItem="Qdf-kG-wgx" firstAttribute="leading" secondItem="mXI-Sg-wYe" secondAttribute="trailing" constant="10" id="7VI-oy-V0w"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="top" secondItem="mXI-Sg-wYe" secondAttribute="bottom" constant="15" id="8UG-sf-OQU"/>
|
||||
<constraint firstItem="mXI-Sg-wYe" firstAttribute="centerY" secondItem="Qdf-kG-wgx" secondAttribute="centerY" id="Ad5-Kd-ox8"/>
|
||||
<constraint firstItem="0d0-9z-5Ar" firstAttribute="centerY" secondItem="mXI-Sg-wYe" secondAttribute="centerY" id="Jlp-rC-N2E"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="trailing" secondItem="Qdf-kG-wgx" secondAttribute="trailing" id="LK6-M8-kCT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="CTY-XM-iLf" secondAttribute="bottom" constant="10" id="MrU-Ar-zkT"/>
|
||||
<constraint firstItem="mXI-Sg-wYe" firstAttribute="leading" secondItem="0d0-9z-5Ar" secondAttribute="trailing" constant="10" id="O4U-Wa-zdT"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="leading" secondItem="0d0-9z-5Ar" secondAttribute="leading" id="UeO-oR-wzQ"/>
|
||||
<constraint firstItem="Qdf-kG-wgx" firstAttribute="top" secondItem="1lx-d3-6c2" secondAttribute="top" constant="20" id="WOL-w8-7T3"/>
|
||||
<constraint firstItem="0d0-9z-5Ar" firstAttribute="leading" secondItem="1lx-d3-6c2" secondAttribute="leading" constant="20" id="cMk-cs-f9i"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Mu-7j-flw">
|
||||
<rect key="frame" x="0.0" y="108" width="375" height="40.5"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="0ME-4K-MWA" customClass="encryption_warning">
|
||||
<rect key="frame" x="20" y="13.5" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="0ME-4K-MWA" secondAttribute="height" multiplier="1:1" id="r0c-Ix-lm4"/>
|
||||
<constraint firstAttribute="width" constant="14" id="zKC-ZT-6y8"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Riot iOS (BLPOWKUPGQ)" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FET-ZT-Q69">
|
||||
<rect key="frame" x="44" y="10" width="311" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0ME-4K-MWA" firstAttribute="leading" secondItem="3Mu-7j-flw" secondAttribute="leading" constant="20" id="8W6-Zu-Qu2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="FET-ZT-Q69" secondAttribute="trailing" constant="20" id="BbB-ge-jNu"/>
|
||||
<constraint firstItem="FET-ZT-Q69" firstAttribute="top" secondItem="3Mu-7j-flw" secondAttribute="top" constant="10" id="Bw3-IL-AwO"/>
|
||||
<constraint firstAttribute="bottom" secondItem="FET-ZT-Q69" secondAttribute="bottom" constant="10" id="NGo-OD-AHt"/>
|
||||
<constraint firstItem="FET-ZT-Q69" firstAttribute="leading" secondItem="0ME-4K-MWA" secondAttribute="trailing" constant="10" id="Sap-sa-3tW"/>
|
||||
<constraint firstItem="0ME-4K-MWA" firstAttribute="centerY" secondItem="FET-ZT-Q69" secondAttribute="centerY" id="W4V-Dz-CKf"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rB7-Ct-Mq7">
|
||||
<rect key="frame" x="0.0" y="148.5" width="375" height="148"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oRN-W6-TVF">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="148"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dd2-N5-GEx">
|
||||
<rect key="frame" x="20" y="10" width="335" height="54"/>
|
||||
<string key="text">Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HiI-uV-Hjw">
|
||||
<rect key="frame" x="20" y="84" width="335" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="LKd-e3-0AI"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="exF-8e-73K"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Manually verify">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="verifyButtonAction:" destination="edG-Ef-G5W" eventType="touchUpInside" id="gGx-OT-X5w"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="HiI-uV-Hjw" secondAttribute="bottom" constant="20" id="3bP-Hs-m42"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dd2-N5-GEx" secondAttribute="trailing" constant="20" id="BHw-Yv-ceG"/>
|
||||
<constraint firstItem="dd2-N5-GEx" firstAttribute="leading" secondItem="oRN-W6-TVF" secondAttribute="leading" constant="20" id="BV7-Vu-PQR"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="top" secondItem="dd2-N5-GEx" secondAttribute="bottom" constant="20" id="BtT-Mg-Okq"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="centerX" secondItem="oRN-W6-TVF" secondAttribute="centerX" id="Ign-Yp-tpB"/>
|
||||
<constraint firstItem="dd2-N5-GEx" firstAttribute="top" secondItem="oRN-W6-TVF" secondAttribute="top" constant="10" id="Ygn-tl-ahK"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HiI-uV-Hjw" secondAttribute="trailing" constant="20" id="nqA-lS-ubx"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="width" secondItem="oRN-W6-TVF" secondAttribute="width" priority="250" id="qp1-z5-eP9"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="oRN-W6-TVF" secondAttribute="leading" constant="20" id="uNl-Ok-uee"/>
|
||||
<constraint firstAttribute="height" priority="250" id="yuI-ch-y92"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="oRN-W6-TVF" firstAttribute="width" secondItem="rB7-Ct-Mq7" secondAttribute="width" id="2pR-3D-RYU"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="3Mu-7j-flw" secondAttribute="trailing" id="W3d-d0-w2v"/>
|
||||
<constraint firstItem="rB7-Ct-Mq7" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="aH4-6p-pgC"/>
|
||||
<constraint firstItem="1lx-d3-6c2" firstAttribute="top" secondItem="sX2-Xu-9f5" secondAttribute="top" id="aTB-di-K6F"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rB7-Ct-Mq7" secondAttribute="bottom" id="dOS-zD-hAw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rB7-Ct-Mq7" secondAttribute="trailing" id="iH7-HJ-6RG"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1lx-d3-6c2" secondAttribute="trailing" id="lLi-mF-pmE"/>
|
||||
<constraint firstItem="1lx-d3-6c2" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="rU8-aC-b2j"/>
|
||||
<constraint firstItem="3Mu-7j-flw" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="thK-xG-Pvw"/>
|
||||
<constraint firstItem="3Mu-7j-flw" firstAttribute="top" secondItem="1lx-d3-6c2" secondAttribute="bottom" id="xjU-4b-Ac0"/>
|
||||
<constraint firstItem="rB7-Ct-Mq7" firstAttribute="top" secondItem="3Mu-7j-flw" secondAttribute="bottom" id="yOc-gk-ilO"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="sX2-Xu-9f5" secondAttribute="trailing" id="9uF-gg-w2a"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="width" secondItem="16P-bA-3wP" secondAttribute="width" id="AbT-qq-n6g"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="leading" secondItem="16P-bA-3wP" secondAttribute="leading" id="pP0-dc-iec"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sX2-Xu-9f5" secondAttribute="bottom" id="tUz-rr-k5B"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="top" secondItem="16P-bA-3wP" secondAttribute="top" id="yrj-Cm-1Zm"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="16P-bA-3wP" secondAttribute="bottom" id="3Ak-zJ-1Ca"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="top" secondItem="cZY-v6-GjO" secondAttribute="top" id="SV3-Ai-4QS"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="leading" secondItem="cZY-v6-GjO" secondAttribute="leading" id="dlk-85-Zca"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="trailing" secondItem="cZY-v6-GjO" secondAttribute="trailing" id="hFM-5p-Ne1"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="cZY-v6-GjO"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="badgeImageView" destination="0d0-9z-5Ar" id="M0i-8Q-Zal"/>
|
||||
<outlet property="closeButton" destination="Qdf-kG-wgx" id="yl7-lg-gW6"/>
|
||||
<outlet property="deviceInformationLabel" destination="FET-ZT-Q69" id="D8n-5b-H4p"/>
|
||||
<outlet property="deviceStatusImageView" destination="0ME-4K-MWA" id="kgp-dT-Q0P"/>
|
||||
<outlet property="informationLabel" destination="CTY-XM-iLf" id="qVC-lS-YOY"/>
|
||||
<outlet property="titleLabel" destination="mXI-Sg-wYe" id="Wrj-QM-srt"/>
|
||||
<outlet property="untrustedSessionContainerView" destination="oRN-W6-TVF" id="9sc-53-MWY"/>
|
||||
<outlet property="untrustedSessionInformationLabel" destination="dd2-N5-GEx" id="6TB-Ve-OyC"/>
|
||||
<outlet property="verifyButton" destination="HiI-uV-Hjw" id="JnR-0Q-aSc"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8Fe-S5-Pf4" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-175.19999999999999" y="124.58770614692655"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="close_button" width="16" height="16"/>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,268 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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 UserVerificationSessionStatusViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let verifyButtonCornerRadius: CGFloat = 8.0
|
||||
static let informationTextDefaultFont = UIFont.systemFont(ofSize: 15.0)
|
||||
static let informationTextBoldFont = UIFont.systemFont(ofSize: 15.0, weight: .medium)
|
||||
static let deviceNameFont = UIFont.systemFont(ofSize: 17.0, weight: .medium)
|
||||
static let deviceIdFont = UIFont.systemFont(ofSize: 15.0)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var badgeImageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var deviceStatusImageView: UIImageView!
|
||||
@IBOutlet private weak var deviceInformationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var untrustedSessionContainerView: UIView!
|
||||
@IBOutlet private weak var untrustedSessionInformationLabel: UILabel!
|
||||
@IBOutlet private weak var verifyButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationSessionStatusViewModelType!
|
||||
private var theme: Theme!
|
||||
private var keyboardAvoider: KeyboardAvoider?
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationSessionStatusViewModelType) -> UserVerificationSessionStatusViewController {
|
||||
let viewController = StoryboardScene.UserVerificationSessionStatusViewController.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.vc_removeBackTitle()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.verifyButton.layer.cornerRadius = Constants.verifyButtonCornerRadius
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.frame.size.width/2
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.closeButton.vc_setBackgroundColor(theme.headerTextSecondaryColor, for: .normal)
|
||||
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.untrustedSessionInformationLabel.textColor = theme.textPrimaryColor
|
||||
self.verifyButton.vc_setBackgroundColor(theme.tintColor, for: .normal)
|
||||
}
|
||||
|
||||
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() {
|
||||
self.closeButton.layer.masksToBounds = true
|
||||
self.verifyButton.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationSessionStatusViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(viewData: let sessionStatusViewData):
|
||||
self.renderLoaded(viewData: sessionStatusViewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: SessionStatusViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
let badgeImage: UIImage
|
||||
let title: String
|
||||
|
||||
if viewData.isDeviceTrusted {
|
||||
badgeImage = Asset.Images.encryptionTrusted.image
|
||||
title = "Trusted"
|
||||
} else {
|
||||
badgeImage = Asset.Images.encryptionWarning.image
|
||||
title = "Warning"
|
||||
}
|
||||
|
||||
let unstrustedInformationText: String
|
||||
let verifyButtonTitle: String
|
||||
|
||||
if viewData.isCurrentUser {
|
||||
unstrustedInformationText = "If you didn’t sign in to this session, your account may be compromised."
|
||||
verifyButtonTitle = "Verify"
|
||||
} else {
|
||||
unstrustedInformationText = "Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it."
|
||||
verifyButtonTitle = "Manually verify"
|
||||
}
|
||||
|
||||
self.badgeImageView.image = badgeImage
|
||||
self.titleLabel.text = title
|
||||
self.informationLabel.attributedText = self.buildInformationAttributedText(with: viewData)
|
||||
|
||||
self.deviceStatusImageView.image = badgeImage
|
||||
self.deviceInformationLabel.attributedText = self.builDeviceInfoAttributedText(with: viewData)
|
||||
|
||||
self.untrustedSessionInformationLabel.text = unstrustedInformationText
|
||||
self.verifyButton.setTitle(verifyButtonTitle, for: .normal)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func buildUserInfoText(with userId: String, userDisplayName: String?) -> String {
|
||||
|
||||
let userInfoText: String
|
||||
|
||||
if let userDisplayName = userDisplayName {
|
||||
userInfoText = "\(userDisplayName) (\(userId))"
|
||||
} else {
|
||||
userInfoText = userId
|
||||
}
|
||||
|
||||
return userInfoText
|
||||
}
|
||||
|
||||
private func buildInformationAttributedText(with viewData: SessionStatusViewData) -> NSAttributedString {
|
||||
|
||||
let informationAttributedText: NSMutableAttributedString = NSMutableAttributedString()
|
||||
|
||||
let informationTextDefaultAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextDefaultFont]
|
||||
|
||||
let informationTextBoldAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextBoldFont]
|
||||
|
||||
let userInfoText = self.buildUserInfoText(with: viewData.userId, userDisplayName: viewData.userDisplayName)
|
||||
|
||||
if viewData.isDeviceTrusted {
|
||||
|
||||
if viewData.isCurrentUser {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: "This session is trusted for secure messaging because you verified it:", attributes: informationTextDefaultAttributes)
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
} else {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: "This device is trusted for secure messaging because ", attributes: informationTextDefaultAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: userInfoText, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart3 = NSAttributedString(string: " verified it:", attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
informationAttributedText.append(informationAttributedStringPart3)
|
||||
}
|
||||
|
||||
} else {
|
||||
if viewData.isCurrentUser {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: "Verify this session to mark it as trusted & grant it access to encrypted messages:", attributes: informationTextDefaultAttributes)
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
} else {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: userInfoText, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: " signed in using a new device:", attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
}
|
||||
}
|
||||
|
||||
return informationAttributedText
|
||||
}
|
||||
|
||||
private func builDeviceInfoAttributedText(with viewData: SessionStatusViewData) -> NSAttributedString {
|
||||
let deviceInfoAttributedText = NSMutableAttributedString()
|
||||
let deviceInfoAttributedTextPart1 = NSAttributedString(string: "\(viewData.deviceName) ", attributes: [.foregroundColor: self.theme.textPrimaryColor, .font: Constants.deviceNameFont])
|
||||
let deviceInfoAttributedTextPart2 = NSAttributedString(string: "(\(viewData.deviceId))", attributes: [.foregroundColor: self.theme.textSecondaryColor, .font: Constants.deviceIdFont])
|
||||
deviceInfoAttributedText.append(deviceInfoAttributedTextPart1)
|
||||
deviceInfoAttributedText.append(deviceInfoAttributedTextPart2)
|
||||
return deviceInfoAttributedText
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func closeButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
|
||||
@IBAction private func verifyButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .verify)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserVerificationSessionStatusViewModelViewDelegate
|
||||
extension UserVerificationSessionStatusViewController: UserVerificationSessionStatusViewModelViewDelegate {
|
||||
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionStatusViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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 SessionStatusViewData {
|
||||
let userId: String
|
||||
let userDisplayName: String?
|
||||
let isCurrentUser: Bool
|
||||
|
||||
let deviceId: String
|
||||
let deviceName: String
|
||||
let isDeviceTrusted: Bool
|
||||
}
|
||||
|
||||
enum UserVerificationSessionStatusViewModelError: Error {
|
||||
case deviceNotFound
|
||||
}
|
||||
|
||||
final class UserVerificationSessionStatusViewModel: UserVerificationSessionStatusViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
private let deviceId: String
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationSessionStatusViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationSessionStatusViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
self.deviceId = deviceId
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationSessionStatusViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .verify:
|
||||
self.coordinatorDelegate?.userVerificationSessionStatusViewModel(self, wantsToManuallyVerifyDeviceWithId: self.deviceId, for: self.userId)
|
||||
case .close:
|
||||
self.coordinatorDelegate?.userVerificationSessionStatusViewModelDidClose(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
guard let deviceInfo = self.session.crypto.device(withDeviceId: self.deviceId, ofUser: self.userId) else {
|
||||
self.update(viewState: .error(UserVerificationSessionStatusViewModelError.deviceNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
let isCurrentUser = self.session.myUser.userId == self.userId
|
||||
|
||||
let viewData = SessionStatusViewData(userId: self.userId,
|
||||
userDisplayName: self.userDisplayName,
|
||||
isCurrentUser: isCurrentUser,
|
||||
deviceId: deviceInfo.deviceId,
|
||||
deviceName: deviceInfo.displayName ?? "",
|
||||
isDeviceTrusted: deviceInfo.trustLevel.isVerified)
|
||||
self.update(viewState: .loaded(viewData: viewData))
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationSessionStatusViewState) {
|
||||
self.viewDelegate?.userVerificationSessionStatusViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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 UserVerificationSessionStatusViewModelViewDelegate: class {
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionStatusViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationSessionStatusViewModelCoordinatorDelegate: class {
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionStatusViewModelDidClose(_ viewModel: UserVerificationSessionStatusViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationSessionStatusViewController`
|
||||
protocol UserVerificationSessionStatusViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationSessionStatusViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationSessionStatusViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationSessionStatusViewAction)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationSessionStatusViewController view state
|
||||
enum UserVerificationSessionStatusViewState {
|
||||
case loading
|
||||
case loaded(viewData: SessionStatusViewData)
|
||||
case error(Error)
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
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
|
||||
import Reusable
|
||||
|
||||
struct UserVerificationSessionStatusViewData {
|
||||
let deviceId: String
|
||||
let sessionName: String
|
||||
let isTrusted: Bool
|
||||
}
|
||||
|
||||
final class UserVerificationSessionStatusCell: UITableViewCell, NibReusable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var statusImageView: UIImageView!
|
||||
@IBOutlet private weak var sessionNameLabel: UILabel!
|
||||
@IBOutlet private weak var statusTextLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewData: UserVerificationSessionStatusViewData?
|
||||
private var theme: Theme?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(viewData: UserVerificationSessionStatusViewData) {
|
||||
self.viewData = viewData
|
||||
|
||||
let statusText: String
|
||||
let statusImage: UIImage
|
||||
|
||||
if viewData.isTrusted {
|
||||
statusImage = Asset.Images.encryptionTrusted.image
|
||||
statusText = "Trusted"
|
||||
} else {
|
||||
statusImage = Asset.Images.encryptionWarning.image
|
||||
statusText = "Not trusted"
|
||||
}
|
||||
|
||||
self.statusImageView.image = statusImage
|
||||
self.statusTextLabel.text = statusText
|
||||
self.sessionNameLabel.text = viewData.sessionName
|
||||
|
||||
self.updateStatusTextColor()
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.backgroundColor = theme.headerBackgroundColor
|
||||
self.sessionNameLabel.textColor = theme.textPrimaryColor
|
||||
self.updateStatusTextColor()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateStatusTextColor() {
|
||||
guard let viewData = self.viewData, let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
self.statusTextLabel.textColor = viewData.isTrusted ? theme.tintColor : theme.warningColor
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="37" id="KGk-i7-Jjw" customClass="UserVerificationSessionStatusCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="36.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="V5p-pi-DJj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="36.5"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="4Ja-9R-o9i" customClass="encryption_warning">
|
||||
<rect key="frame" x="20" y="11.5" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="4Ja-9R-o9i" secondAttribute="height" multiplier="1:1" id="Ivd-S1-mib"/>
|
||||
<constraint firstAttribute="width" constant="14" id="n0o-Xb-nnn"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Riot iOS" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MkZ-9n-kCS">
|
||||
<rect key="frame" x="44" y="10" width="174.5" height="16.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="Trusted" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsp-qe-DLy">
|
||||
<rect key="frame" x="228.5" y="8" width="58.5" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.01176470588" green="0.70196078429999997" blue="0.50588235290000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="MkZ-9n-kCS" firstAttribute="leading" secondItem="4Ja-9R-o9i" secondAttribute="trailing" constant="10" id="29O-OV-c6O"/>
|
||||
<constraint firstItem="4Ja-9R-o9i" firstAttribute="leading" secondItem="V5p-pi-DJj" secondAttribute="leading" constant="20" id="Gcu-EC-b72"/>
|
||||
<constraint firstItem="wsp-qe-DLy" firstAttribute="centerY" secondItem="MkZ-9n-kCS" secondAttribute="centerY" id="JQR-uR-OSm"/>
|
||||
<constraint firstAttribute="bottom" secondItem="MkZ-9n-kCS" secondAttribute="bottom" constant="10" id="Ohk-Zx-SaA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wsp-qe-DLy" secondAttribute="trailing" constant="20" id="Onn-ew-tSg"/>
|
||||
<constraint firstItem="4Ja-9R-o9i" firstAttribute="centerY" secondItem="MkZ-9n-kCS" secondAttribute="centerY" id="U88-nG-bpa"/>
|
||||
<constraint firstItem="wsp-qe-DLy" firstAttribute="leading" secondItem="MkZ-9n-kCS" secondAttribute="trailing" constant="10" id="VdB-e0-Ru9"/>
|
||||
<constraint firstItem="MkZ-9n-kCS" firstAttribute="top" secondItem="V5p-pi-DJj" secondAttribute="top" constant="10" id="lx8-Ae-MX8"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="V5p-pi-DJj" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="3rQ-mA-tnY"/>
|
||||
<constraint firstItem="V5p-pi-DJj" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="PDK-oM-MhJ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="V5p-pi-DJj" secondAttribute="bottom" id="dj0-s9-48N"/>
|
||||
<constraint firstAttribute="trailing" secondItem="V5p-pi-DJj" secondAttribute="trailing" id="lp6-TZ-ycQ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<connections>
|
||||
<outlet property="sessionNameLabel" destination="MkZ-9n-kCS" id="atH-sc-sJx"/>
|
||||
<outlet property="statusImageView" destination="4Ja-9R-o9i" id="n6o-LV-b3O"/>
|
||||
<outlet property="statusTextLabel" destination="wsp-qe-DLy" id="4dV-vY-pmR"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-72" y="26"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,70 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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 UserVerificationSessionsStatusCoordinator: UserVerificationSessionsStatusCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var userVerificationSessionsStatusViewModel: UserVerificationSessionsStatusViewModelType
|
||||
private let userVerificationSessionsStatusViewController: UserVerificationSessionsStatusViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationSessionsStatusCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String) {
|
||||
self.session = session
|
||||
|
||||
let userVerificationSessionsStatusViewModel = UserVerificationSessionsStatusViewModel(session: self.session, userId: userId)
|
||||
let userVerificationSessionsStatusViewController = UserVerificationSessionsStatusViewController.instantiate(with: userVerificationSessionsStatusViewModel)
|
||||
self.userVerificationSessionsStatusViewModel = userVerificationSessionsStatusViewModel
|
||||
self.userVerificationSessionsStatusViewController = userVerificationSessionsStatusViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationSessionsStatusViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationSessionsStatusViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusViewModelCoordinatorDelegate
|
||||
extension UserVerificationSessionsStatusCoordinator: UserVerificationSessionsStatusViewModelCoordinatorDelegate {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didSelectDeviceWithId deviceId: String, for userId: String) {
|
||||
self.delegate?.userVerificationSessionsStatusCoordinator(self, didSelectDeviceWithId: deviceId, for: userId)
|
||||
}
|
||||
|
||||
func userVerificationSessionsStatusViewModelDidClose(_ viewModel: UserVerificationSessionsStatusViewModelType) {
|
||||
self.delegate?.userVerificationSessionsStatusCoordinatorDidClose(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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 UserVerificationSessionsStatusCoordinatorDelegate: class {
|
||||
func userVerificationSessionsStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionsStatusCoordinatorType)
|
||||
func userVerificationSessionsStatusCoordinator(_ coordinator: UserVerificationSessionsStatusCoordinatorType, didSelectDeviceWithId deviceId: String, for userId: String)
|
||||
}
|
||||
|
||||
/// `UserVerificationSessionsStatusCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationSessionsStatusCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationSessionsStatusCoordinatorDelegate? { get }
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationSessionsStatusViewController view actions exposed to view model
|
||||
enum UserVerificationSessionsStatusViewAction {
|
||||
case loadData
|
||||
case selectSession(deviceId: String)
|
||||
case close
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="le3-e8-C1h">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Sessions Status View Controller-->
|
||||
<scene sceneID="G1j-dD-ArC">
|
||||
<objects>
|
||||
<viewController id="le3-e8-C1h" customClass="UserVerificationSessionsStatusViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="IXd-J2-Vex">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Kfr-Ho-krK">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="108"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NBJ-A1-IP0">
|
||||
<rect key="frame" x="325" y="20" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="jda-sE-4Uz"/>
|
||||
<constraint firstAttribute="height" constant="30" id="rPb-uQ-Tyt"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" image="close_button"/>
|
||||
<connections>
|
||||
<action selector="closeButtonAction:" destination="le3-e8-C1h" eventType="touchUpInside" id="4JW-Rl-hIk"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="rZH-h1-WTw">
|
||||
<rect key="frame" x="20" y="23" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="rZH-h1-WTw" secondAttribute="height" multiplier="1:1" id="RgU-aV-cmS"/>
|
||||
<constraint firstAttribute="height" constant="24" id="v0F-Dp-CmF"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Warning" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcs-Vl-AtB">
|
||||
<rect key="frame" x="54" y="23" width="261" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Messages with this user in this room are end-to-end encrypted and can’t be read by third parties." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vfc-7p-LLs">
|
||||
<rect key="frame" x="20" y="62" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NBJ-A1-IP0" firstAttribute="leading" secondItem="fcs-Vl-AtB" secondAttribute="trailing" constant="10" id="0cd-68-5tR"/>
|
||||
<constraint firstItem="fcs-Vl-AtB" firstAttribute="leading" secondItem="rZH-h1-WTw" secondAttribute="trailing" constant="10" id="75Z-TB-Hhj"/>
|
||||
<constraint firstItem="fcs-Vl-AtB" firstAttribute="centerY" secondItem="NBJ-A1-IP0" secondAttribute="centerY" id="DVH-2n-tXo"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="trailing" secondItem="NBJ-A1-IP0" secondAttribute="trailing" id="KQg-I1-5FP"/>
|
||||
<constraint firstItem="rZH-h1-WTw" firstAttribute="centerY" secondItem="fcs-Vl-AtB" secondAttribute="centerY" id="N8y-sA-Am7"/>
|
||||
<constraint firstItem="NBJ-A1-IP0" firstAttribute="top" secondItem="Kfr-Ho-krK" secondAttribute="top" constant="20" id="VUJ-Jj-UjR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="NBJ-A1-IP0" secondAttribute="trailing" constant="20" id="ZH0-2n-XKy"/>
|
||||
<constraint firstItem="rZH-h1-WTw" firstAttribute="leading" secondItem="Kfr-Ho-krK" secondAttribute="leading" constant="20" id="gPw-ja-veq"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="top" secondItem="fcs-Vl-AtB" secondAttribute="bottom" constant="15" id="qh5-nR-1EN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Vfc-7p-LLs" secondAttribute="bottom" constant="10" id="t5D-ht-cSc"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="leading" secondItem="rZH-h1-WTw" secondAttribute="leading" id="zAt-yf-7HQ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sessions" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uMA-Dt-xTz">
|
||||
<rect key="frame" x="20" y="133" width="335" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="drr-Gd-5wA">
|
||||
<rect key="frame" x="0.0" y="167" width="375" height="500"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="le3-e8-C1h" id="Xmr-jN-S8s"/>
|
||||
<outlet property="delegate" destination="le3-e8-C1h" id="Y5J-nA-Ynp"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="drr-Gd-5wA" firstAttribute="leading" secondItem="IXd-J2-Vex" secondAttribute="leading" id="52r-4B-JdK"/>
|
||||
<constraint firstItem="Kfr-Ho-krK" firstAttribute="trailing" secondItem="Eud-3V-Vqw" secondAttribute="trailing" id="9Zg-EV-cHL"/>
|
||||
<constraint firstItem="Eud-3V-Vqw" firstAttribute="trailing" secondItem="uMA-Dt-xTz" secondAttribute="trailing" constant="20" id="Amb-5F-Ors"/>
|
||||
<constraint firstAttribute="trailing" secondItem="drr-Gd-5wA" secondAttribute="trailing" id="Awf-hM-uzJ"/>
|
||||
<constraint firstItem="drr-Gd-5wA" firstAttribute="top" secondItem="uMA-Dt-xTz" secondAttribute="bottom" constant="10" id="H1a-sI-AbP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="drr-Gd-5wA" secondAttribute="bottom" id="Nhh-Ps-Upr"/>
|
||||
<constraint firstItem="Kfr-Ho-krK" firstAttribute="top" secondItem="Eud-3V-Vqw" secondAttribute="top" id="Wxs-Uw-xoA"/>
|
||||
<constraint firstItem="uMA-Dt-xTz" firstAttribute="leading" secondItem="Eud-3V-Vqw" secondAttribute="leading" constant="20" id="an1-BC-BP0"/>
|
||||
<constraint firstItem="uMA-Dt-xTz" firstAttribute="top" secondItem="Kfr-Ho-krK" secondAttribute="bottom" constant="5" id="wok-Oq-W0z"/>
|
||||
<constraint firstItem="Eud-3V-Vqw" firstAttribute="leading" secondItem="Kfr-Ho-krK" secondAttribute="leading" id="xjg-JA-ZUT"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Eud-3V-Vqw"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="badgeImageImageView" destination="rZH-h1-WTw" id="M2a-7i-OoW"/>
|
||||
<outlet property="closeButton" destination="NBJ-A1-IP0" id="AYv-b7-fFp"/>
|
||||
<outlet property="informationLabel" destination="Vfc-7p-LLs" id="em9-jE-1wo"/>
|
||||
<outlet property="sessionsTableViewTitle" destination="uMA-Dt-xTz" id="vFU-UV-jMO"/>
|
||||
<outlet property="tableView" destination="drr-Gd-5wA" id="Cs0-pF-wwB"/>
|
||||
<outlet property="titleLabel" destination="fcs-Vl-AtB" id="iGp-kn-QfJ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dBA-Zm-evE" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-463.19999999999999" y="-80.50974512743629"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="close_button" width="16" height="16"/>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,234 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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 UserVerificationSessionsStatusViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let estimatedRowHeight: CGFloat = 40.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var badgeImageImageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
@IBOutlet private weak var sessionsTableViewTitle: UILabel!
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationSessionsStatusViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityIndicatorPresenter: ActivityIndicatorPresenter!
|
||||
private var sessionsStatusViewData: [UserVerificationSessionStatusViewData] = []
|
||||
private var userEncryptionTrustLevel: UserEncryptionTrustLevel = .unknown
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationSessionsStatusViewModelType) -> UserVerificationSessionsStatusViewController {
|
||||
let viewController = StoryboardScene.UserVerificationSessionsStatusViewController.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.vc_removeBackTitle()
|
||||
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.frame.size.width/2
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if let selectedIndexPath = self.tableView.indexPathForSelectedRow {
|
||||
self.tableView.deselectRow(at: selectedIndexPath, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.closeButton.vc_setBackgroundColor(theme.headerTextSecondaryColor, for: .normal)
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
self.sessionsTableViewTitle.textColor = theme.textPrimaryColor
|
||||
}
|
||||
|
||||
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() {
|
||||
self.closeButton.layer.masksToBounds = true
|
||||
self.setupTableView()
|
||||
self.updateTitleViews()
|
||||
|
||||
self.sessionsTableViewTitle.text = "Sessions"
|
||||
self.informationLabel.text = "Messages with this user in this room are end-to-end encrypted and can’t be read by third parties."
|
||||
}
|
||||
|
||||
private func setupTableView() {
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedRowHeight = Constants.estimatedRowHeight
|
||||
self.tableView.separatorStyle = .none
|
||||
self.tableView.tableFooterView = UIView()
|
||||
self.tableView.alwaysBounceVertical = false
|
||||
|
||||
self.tableView.register(cellType: UserVerificationSessionStatusCell.self)
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationSessionsStatusViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(userTrustLevel: let userTrustLevel, sessionsStatusViewData: let sessionsStatusViewData):
|
||||
self.renderLoaded(userTrustLevel: userTrustLevel, sessionsStatusViewData: sessionsStatusViewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.tableView.isUserInteractionEnabled = false
|
||||
self.activityIndicatorPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(userTrustLevel: UserEncryptionTrustLevel, sessionsStatusViewData: [UserVerificationSessionStatusViewData]) {
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.tableView.isUserInteractionEnabled = true
|
||||
|
||||
self.userEncryptionTrustLevel = userTrustLevel
|
||||
self.sessionsStatusViewData = sessionsStatusViewData
|
||||
|
||||
self.updateTitleViews()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.tableView.isUserInteractionEnabled = true
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func updateTitleViews() {
|
||||
|
||||
let badgeImage: UIImage
|
||||
let title: String
|
||||
|
||||
switch self.userEncryptionTrustLevel {
|
||||
case .trusted:
|
||||
badgeImage = Asset.Images.encryptionTrusted.image
|
||||
title = "Trusted"
|
||||
case .warning:
|
||||
badgeImage = Asset.Images.encryptionWarning.image
|
||||
title = "Warning"
|
||||
default:
|
||||
badgeImage = Asset.Images.encryptionNormal.image
|
||||
title = "Unknown"
|
||||
}
|
||||
|
||||
self.badgeImageImageView.image = badgeImage
|
||||
self.titleLabel.text = title
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func closeButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension UserVerificationSessionsStatusViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.sessionsStatusViewData.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: UserVerificationSessionStatusCell.self)
|
||||
|
||||
let viewData = self.sessionsStatusViewData[indexPath.row]
|
||||
|
||||
cell.update(theme: self.theme)
|
||||
cell.fill(viewData: viewData)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension UserVerificationSessionsStatusViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let viewData = self.sessionsStatusViewData[indexPath.row]
|
||||
self.viewModel.process(viewAction: .selectSession(deviceId: viewData.deviceId))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusViewModelViewDelegate
|
||||
extension UserVerificationSessionsStatusViewController: UserVerificationSessionsStatusViewModelViewDelegate {
|
||||
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionsStatusViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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 UserVerificationSessionsStatusViewModelError: Error {
|
||||
case unknown
|
||||
}
|
||||
|
||||
final class UserVerificationSessionsStatusViewModel: UserVerificationSessionsStatusViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private var currentOperation: MXHTTPOperation?
|
||||
private var userTrustLevel: UserEncryptionTrustLevel
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationSessionsStatusViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationSessionsStatusViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userTrustLevel = .unknown
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.currentOperation?.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationSessionsStatusViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .selectSession(deviceId: let deviceId):
|
||||
self.coordinatorDelegate?.userVerificationSessionsStatusViewModel(self, didSelectDeviceWithId: deviceId, for: self.userId)
|
||||
case .close:
|
||||
self.coordinatorDelegate?.userVerificationSessionsStatusViewModelDidClose(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
|
||||
let sessionsStatusViewData = self.getSessionStatusViewDataListFromCache(for: self.userId)
|
||||
self.update(viewState: .loaded(userTrustLevel: self.userTrustLevel, sessionsStatusViewData: sessionsStatusViewData))
|
||||
|
||||
self.fetchSessionStatus()
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationSessionsStatusViewState) {
|
||||
self.viewDelegate?.userVerificationSessionsStatusViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func fetchSessionStatus() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.currentOperation = self.getSessionStatusViewDataList(for: self.userId) { result in
|
||||
switch result {
|
||||
case .success(let sessionsStatusViewData):
|
||||
|
||||
let isUserTrusted = sessionsStatusViewData.contains(where: { sessionsStatusViewData -> Bool in
|
||||
return sessionsStatusViewData.isTrusted == false
|
||||
}) == false
|
||||
|
||||
let userTrustLevel: UserEncryptionTrustLevel = isUserTrusted ? .trusted : .warning
|
||||
|
||||
self.update(viewState: .loaded(userTrustLevel: userTrustLevel, sessionsStatusViewData: sessionsStatusViewData))
|
||||
case .failure(let error):
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func getSessionStatusViewDataListFromCache(for userId: String) -> [UserVerificationSessionStatusViewData] {
|
||||
let deviceInfoList = self.getDevicesFromCache(for: self.userId)
|
||||
return self.sessionStatusViewDataList(from: deviceInfoList)
|
||||
}
|
||||
|
||||
private func getDevicesFromCache(for userId: String) -> [MXDeviceInfo] {
|
||||
guard let deviceInfoMap = self.session.crypto.devices(forUser: self.userId) else {
|
||||
return []
|
||||
}
|
||||
return Array(deviceInfoMap.values)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private func getSessionStatusViewDataList(for userId: String, completion: @escaping (Result<[UserVerificationSessionStatusViewData], Error>) -> Void) -> MXHTTPOperation? {
|
||||
|
||||
let httpOperation: MXHTTPOperation?
|
||||
|
||||
httpOperation = self.session.crypto.downloadKeys([self.userId], forceDownload: false, success: { ( usersDeviceMap: MXUsersDevicesMap<MXDeviceInfo>?, usersCrossSigningMap: [String : MXCrossSigningInfo]?) in
|
||||
|
||||
let sessionsViewData: [UserVerificationSessionStatusViewData]
|
||||
|
||||
if let usersDeviceMap = usersDeviceMap, let userDeviceInfoMap = Array(usersDeviceMap.map.values).first {
|
||||
let deviceInfoList = Array(userDeviceInfoMap.values)
|
||||
sessionsViewData = self.sessionStatusViewDataList(from: deviceInfoList)
|
||||
} else {
|
||||
sessionsViewData = []
|
||||
}
|
||||
|
||||
completion(.success(sessionsViewData))
|
||||
|
||||
}, failure: { error in
|
||||
|
||||
let finalError = error ?? UserVerificationSessionsStatusViewModelError.unknown
|
||||
completion(.failure(finalError))
|
||||
})
|
||||
|
||||
return httpOperation
|
||||
}
|
||||
|
||||
private func sessionStatusViewData(from deviceInfo: MXDeviceInfo) -> UserVerificationSessionStatusViewData {
|
||||
return UserVerificationSessionStatusViewData(deviceId: deviceInfo.deviceId, sessionName: deviceInfo.displayName ?? "", isTrusted: deviceInfo.trustLevel.isVerified)
|
||||
}
|
||||
|
||||
private func sessionStatusViewDataList(from deviceInfoList: [MXDeviceInfo]) -> [UserVerificationSessionStatusViewData] {
|
||||
return deviceInfoList.map { (deviceInfo) -> UserVerificationSessionStatusViewData in
|
||||
return self.sessionStatusViewData(from: deviceInfo)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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 UserVerificationSessionsStatusViewModelViewDelegate: class {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionsStatusViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationSessionsStatusViewModelCoordinatorDelegate: class {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didSelectDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionsStatusViewModelDidClose(_ viewModel: UserVerificationSessionsStatusViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationSessionsStatusViewController`
|
||||
protocol UserVerificationSessionsStatusViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationSessionsStatusViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationSessionsStatusViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationSessionsStatusViewAction)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationSessionsStatusViewController view state
|
||||
enum UserVerificationSessionsStatusViewState {
|
||||
case loading
|
||||
case loaded(userTrustLevel: UserEncryptionTrustLevel, sessionsStatusViewData: [UserVerificationSessionStatusViewData])
|
||||
case error(Error)
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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 UserVerificationStartCoordinator: UserVerificationStartCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomMember: MXRoomMember
|
||||
|
||||
private var userVerificationStartViewModel: UserVerificationStartViewModelType
|
||||
private let userVerificationStartViewController: UserVerificationStartViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationStartCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomMember: MXRoomMember) {
|
||||
self.session = session
|
||||
self.roomMember = roomMember
|
||||
|
||||
let userVerificationStartViewModel = UserVerificationStartViewModel(session: self.session, roomMember: self.roomMember)
|
||||
let userVerificationStartViewController = UserVerificationStartViewController.instantiate(with: userVerificationStartViewModel)
|
||||
self.userVerificationStartViewModel = userVerificationStartViewModel
|
||||
self.userVerificationStartViewController = userVerificationStartViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationStartViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationStartViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationStartViewModelCoordinatorDelegate
|
||||
extension UserVerificationStartCoordinator: UserVerificationStartViewModelCoordinatorDelegate {
|
||||
func userVerificationStartViewModelDidCancel(_ viewModel: UserVerificationStartViewModelType) {
|
||||
self.delegate?.userVerificationStartCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didCompleteWithIncomingTransaction transaction: MXSASTransaction) {
|
||||
self.delegate?.userVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
}
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.userVerificationStartCoordinator(self, didTransactionCancelled: transaction)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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 UserVerificationStartCoordinatorDelegate: class {
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartCoordinatorDidCancel(_ coordinator: UserVerificationStartCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationStartCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationStartCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationStartCoordinatorDelegate? { get }
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationStartViewController view actions exposed to view model
|
||||
enum UserVerificationStartViewAction {
|
||||
case loadData
|
||||
case startVerification
|
||||
case cancel
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Start View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="UserVerificationStartViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dQD-Vs-7h6">
|
||||
<rect key="frame" x="0.0" y="254" width="375" height="179"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For extra security, verify @user:matrix.org by checking a one-time code on both your devices." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b2K-LP-jqv">
|
||||
<rect key="frame" x="20" y="10" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for User…" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0QC-yi-0hN">
|
||||
<rect key="frame" x="20" y="78" width="335" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3sZ-h5-05D">
|
||||
<rect key="frame" x="20" y="66" width="335" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="2qO-rj-st6"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="8kK-yb-srh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Start Verification">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="startVerificationButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="3Nd-Km-ucJ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To be secure, do this in person or use another way to communicate." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vli-IZ-UjP">
|
||||
<rect key="frame" x="20" y="130" width="335" height="29"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="centerX" secondItem="dQD-Vs-7h6" secondAttribute="centerX" id="2OM-Zh-gnO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0QC-yi-0hN" secondAttribute="trailing" constant="20" id="70Z-qZ-AG4"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="3sZ-h5-05D" secondAttribute="trailing" constant="20" id="ABg-Iy-IdN"/>
|
||||
<constraint firstItem="Vli-IZ-UjP" firstAttribute="top" secondItem="3sZ-h5-05D" secondAttribute="bottom" constant="20" id="BIE-UM-I0Z"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="top" secondItem="b2K-LP-jqv" secondAttribute="bottom" constant="20" id="HCf-2F-jBr"/>
|
||||
<constraint firstItem="Vli-IZ-UjP" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="Nz0-mv-fF5"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="width" secondItem="dQD-Vs-7h6" secondAttribute="width" priority="250" id="RuV-mq-5JZ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Vli-IZ-UjP" secondAttribute="trailing" constant="20" id="XXe-5j-SxE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Vli-IZ-UjP" secondAttribute="bottom" constant="20" id="Yfh-kG-UKJ"/>
|
||||
<constraint firstItem="b2K-LP-jqv" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="dgE-c5-D4E"/>
|
||||
<constraint firstItem="0QC-yi-0hN" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="g4e-WC-LQ7"/>
|
||||
<constraint firstItem="0QC-yi-0hN" firstAttribute="centerY" secondItem="3sZ-h5-05D" secondAttribute="centerY" id="hqE-9r-yhx"/>
|
||||
<constraint firstAttribute="height" priority="250" id="j1W-Y7-dYc"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="oP3-BI-kYI"/>
|
||||
<constraint firstAttribute="trailing" secondItem="b2K-LP-jqv" secondAttribute="trailing" constant="20" id="uJ5-fk-7Xw"/>
|
||||
<constraint firstItem="b2K-LP-jqv" firstAttribute="top" secondItem="dQD-Vs-7h6" secondAttribute="top" constant="10" id="vlb-Yg-HWD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="centerY" secondItem="bFg-jh-JZB" secondAttribute="centerY" id="GmI-7r-ejI"/>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="trailing" secondItem="bFg-jh-JZB" secondAttribute="trailing" id="XX2-fS-8cb"/>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="yKt-js-MnW"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="additionalInformationLabel" destination="Vli-IZ-UjP" id="4UB-LL-Om3"/>
|
||||
<outlet property="informationLabel" destination="b2K-LP-jqv" id="O4J-7t-Dn6"/>
|
||||
<outlet property="startVerificationButton" destination="3sZ-h5-05D" id="vNi-m9-x7g"/>
|
||||
<outlet property="verificationWaitingLabel" destination="0QC-yi-0hN" id="Qoz-cV-Su7"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3198" y="-647"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -0,0 +1,229 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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 UserVerificationStartViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let verifyButtonCornerRadius: CGFloat = 8.0
|
||||
static let informationTextDefaultFont = UIFont.systemFont(ofSize: 15.0)
|
||||
static let informationTextBoldFont = UIFont.systemFont(ofSize: 15.0, weight: .medium)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var startVerificationButton: UIButton!
|
||||
@IBOutlet private weak var verificationWaitingLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var additionalInformationLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationStartViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationStartViewModelType) -> UserVerificationStartViewController {
|
||||
let viewController = StoryboardScene.UserVerificationStartViewController.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.title = "Verify user"
|
||||
|
||||
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 var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.startVerificationButton.layer.cornerRadius = Constants.verifyButtonCornerRadius
|
||||
}
|
||||
|
||||
// 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.startVerificationButton.vc_setBackgroundColor(theme.tintColor, for: .normal)
|
||||
self.verificationWaitingLabel.textColor = theme.textSecondaryColor
|
||||
self.additionalInformationLabel.textColor = theme.textSecondaryColor
|
||||
}
|
||||
|
||||
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?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
self.startVerificationButton.layer.masksToBounds = true
|
||||
self.startVerificationButton.setTitle("Start verification", for: .normal)
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationStartViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(let viewData):
|
||||
self.renderLoaded(viewData: viewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
case .verificationPending:
|
||||
self.renderVerificationPending()
|
||||
case .cancelled(let reason):
|
||||
self.renderCancelled(reason: reason)
|
||||
case .cancelledByMe(let reason):
|
||||
self.renderCancelledByMe(reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: UserVerificationStartViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.informationLabel.attributedText = self.buildInformationAttributedText(with: viewData.userId)
|
||||
self.verificationWaitingLabel.text = self.buildVerificationWaitingText(with: viewData)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func renderVerificationPending() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.startVerificationButton.isHidden = true
|
||||
self.verificationWaitingLabel.isHidden = false
|
||||
}
|
||||
|
||||
private func renderCancelled(reason: MXTransactionCancelCode) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderCancelledByMe(reason: MXTransactionCancelCode) {
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable), animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
} else {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func buildInformationAttributedText(with userId: String) -> NSAttributedString {
|
||||
|
||||
let informationAttributedText: NSMutableAttributedString = NSMutableAttributedString()
|
||||
|
||||
let informationTextDefaultAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextDefaultFont]
|
||||
|
||||
let informationTextBoldAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextBoldFont]
|
||||
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: "For extra security, verify ", attributes: informationTextDefaultAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: userId, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart3 = NSAttributedString(string: " by checking a one-time code on both your devices.", attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
informationAttributedText.append(informationAttributedStringPart3)
|
||||
|
||||
return informationAttributedText
|
||||
}
|
||||
|
||||
private func buildVerificationWaitingText(with viewData: UserVerificationStartViewData) -> String {
|
||||
let userName = viewData.userDisplayName ?? viewData.userId
|
||||
return "Waiting for \(userName)…"
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func startVerificationButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .startVerification)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserVerificationStartViewModelViewDelegate
|
||||
extension UserVerificationStartViewController: UserVerificationStartViewModelViewDelegate {
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didUpdateViewState viewSate: UserVerificationStartViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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 UserVerificationStartViewModelError: Error {
|
||||
case keyVerificationRequestExpired
|
||||
}
|
||||
|
||||
struct UserVerificationStartViewData {
|
||||
let userId: String
|
||||
let userDisplayName: String?
|
||||
let userAvatarURL: String?
|
||||
}
|
||||
|
||||
final class UserVerificationStartViewModel: UserVerificationStartViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomMember: MXRoomMember
|
||||
private let verificationManager: MXDeviceVerificationManager
|
||||
|
||||
private var keyVerificationRequest: MXKeyVerificationRequest?
|
||||
|
||||
private var viewData: UserVerificationStartViewData {
|
||||
return UserVerificationStartViewData(userId: self.roomMember.userId, userDisplayName: self.roomMember.displayname, userAvatarURL: self.roomMember.avatarUrl)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationStartViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationStartViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomMember: MXRoomMember) {
|
||||
self.session = session
|
||||
self.verificationManager = session.crypto.deviceVerificationManager
|
||||
self.roomMember = roomMember
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationStartViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .startVerification:
|
||||
self.startVerification()
|
||||
case .cancel:
|
||||
self.cancelKeyVerificationRequest()
|
||||
self.coordinatorDelegate?.userVerificationStartViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
self.update(viewState: .loaded(self.viewData))
|
||||
}
|
||||
|
||||
private func startVerification() {
|
||||
self.update(viewState: .verificationPending)
|
||||
|
||||
self.verificationManager.requestVerificationByDM(withUserId: self.roomMember.userId,
|
||||
roomId: nil,
|
||||
fallbackText: "",
|
||||
methods: [MXKeyVerificationMethodSAS],
|
||||
success: { [weak self] (keyVerificationRequest) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.keyVerificationRequest = keyVerificationRequest
|
||||
self.update(viewState: .loaded(self.viewData))
|
||||
self.registerKeyVerificationDidChangeNotification(keyVerificationRequest: keyVerificationRequest)
|
||||
self.registerTransactionDidStateChangeNotification()
|
||||
|
||||
}, failure: { [weak self] error in
|
||||
self?.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationStartViewState) {
|
||||
self.viewDelegate?.userVerificationStartViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func cancelKeyVerificationRequest() {
|
||||
guard let keyVerificationRequest = self.keyVerificationRequest else {
|
||||
return
|
||||
}
|
||||
|
||||
keyVerificationRequest.cancel(with: MXTransactionCancelCode.user(), success: nil, failure: nil)
|
||||
}
|
||||
|
||||
// MARK: - MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: .MXDeviceVerificationTransactionDidChange, object: nil)
|
||||
}
|
||||
|
||||
private func unregisterTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXDeviceVerificationTransactionDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXIncomingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let keyVerificationRequest = self.keyVerificationRequest,
|
||||
let transactionDMEventId = transaction.dmEventId,
|
||||
keyVerificationRequest.requestId == transactionDMEventId else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.coordinatorDelegate?.userVerificationStartViewModel(self, didCompleteWithIncomingTransaction: transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerKeyVerificationDidChangeNotification(keyVerificationRequest: MXKeyVerificationRequest) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(keyVerificationRequestDidChange(notification:)), name: .MXKeyVerificationRequestDidChange, object: keyVerificationRequest)
|
||||
}
|
||||
|
||||
private func unregisterKeyVerificationDidChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationRequestDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func keyVerificationRequestDidChange(notification: Notification) {
|
||||
guard let keyVerificationRequest = notification.object as? MXKeyVerificationByDMRequest else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let currentKeyVerificationRequest = self.keyVerificationRequest, keyVerificationRequest.requestId == currentKeyVerificationRequest.requestId else {
|
||||
return
|
||||
}
|
||||
|
||||
switch keyVerificationRequest.state {
|
||||
case MXKeyVerificationRequestStateAccepted:
|
||||
self.unregisterKeyVerificationDidChangeNotification()
|
||||
case MXKeyVerificationRequestStateCancelled:
|
||||
guard let reason = keyVerificationRequest.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterKeyVerificationDidChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXKeyVerificationRequestStateCancelledByMe:
|
||||
guard let reason = keyVerificationRequest.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterKeyVerificationDidChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
case MXKeyVerificationRequestStateExpired:
|
||||
self.unregisterKeyVerificationDidChangeNotification()
|
||||
self.update(viewState: .error(UserVerificationStartViewModelError.keyVerificationRequestExpired))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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 UserVerificationStartViewModelViewDelegate: class {
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didUpdateViewState viewSate: UserVerificationStartViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationStartViewModelCoordinatorDelegate: class {
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didCompleteWithIncomingTransaction transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartViewModelDidCancel(_ viewModel: UserVerificationStartViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationStartViewController`
|
||||
protocol UserVerificationStartViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationStartViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationStartViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationStartViewAction)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
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
|
||||
|
||||
/// UserVerificationStartViewController view state
|
||||
enum UserVerificationStartViewState {
|
||||
case loading
|
||||
case loaded(UserVerificationStartViewData)
|
||||
case verificationPending
|
||||
case cancelled(MXTransactionCancelCode)
|
||||
case cancelledByMe(MXTransactionCancelCode)
|
||||
case error(Error)
|
||||
}
|
143
Riot/Modules/UserVerification/UserVerificationCoordinator.swift
Normal file
143
Riot/Modules/UserVerification/UserVerificationCoordinator.swift
Normal file
|
@ -0,0 +1,143 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
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
|
||||
|
||||
@objcMembers
|
||||
final class UserVerificationCoordinator: NSObject, UserVerificationCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let presenter: Presentable
|
||||
private let navigationRouter: NavigationRouterType
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(presenter: Presentable, session: MXSession, userId: String, userDisplayName: String?) {
|
||||
self.presenter = presenter
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
// Do not start again if existing coordinators are presented
|
||||
guard self.childCoordinators.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let rootCoordinator = UserVerificationSessionsStatusCoordinator(session: self.session, userId: self.userId)
|
||||
rootCoordinator.delegate = self
|
||||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
|
||||
self.navigationRouter.setRootModule(rootCoordinator, hideNavigationBar: true, animated: false, popCompletion: {
|
||||
self.remove(childCoordinator: rootCoordinator)
|
||||
})
|
||||
|
||||
let rootViewController = self.navigationRouter.toPresentable()
|
||||
rootViewController.modalPresentationStyle = .formSheet
|
||||
|
||||
self.presenter.toPresentable().present(rootViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func presentSessionStatus(with deviceId: String, for userId: String, userDisplayName: String?) {
|
||||
let coordinator = UserVerificationSessionStatusCoordinator(session: self.session, userId: userId, userDisplayName: userDisplayName, deviceId: deviceId)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.navigationRouter.push(coordinator, animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentDeviceVerification(for deviceId: String) {
|
||||
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(navigationRouter: self.navigationRouter, session: self.session, userId: self.userId, otherDeviceId: deviceId)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: deviceVerificationCoordinator)
|
||||
|
||||
self.navigationRouter.push(deviceVerificationCoordinator, animated: true, popCompletion: {
|
||||
self.remove(childCoordinator: deviceVerificationCoordinator)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: UserVerificationSessionsStatusCoordinatorDelegate {
|
||||
func userVerificationSessionsStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionsStatusCoordinatorType) {
|
||||
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
func userVerificationSessionsStatusCoordinator(_ coordinator: UserVerificationSessionsStatusCoordinatorType, didSelectDeviceWithId deviceId: String, for userId: String) {
|
||||
self.presentSessionStatus(with: deviceId, for: userId, userDisplayName: self.userDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionStatusCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: UserVerificationSessionStatusCoordinatorDelegate {
|
||||
|
||||
func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) {
|
||||
|
||||
self.presentDeviceVerification(for: deviceId)
|
||||
}
|
||||
|
||||
func userVerificationSessionStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionStatusCoordinatorType) {
|
||||
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: DeviceVerificationCoordinatorDelegate {
|
||||
|
||||
func deviceVerificationCoordinatorDidComplete(_ coordinator: DeviceVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
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
|
||||
|
||||
@objc protocol UserVerificationCoordinatorBridgePresenterDelegate {
|
||||
func userVerificationCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: UserVerificationCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// UserVerificationCoordinatorBridgePresenter enables to start UserVerificationCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class UserVerificationCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let presenter: Presentable
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
|
||||
private var coordinator: Coordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: UserVerificationCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(presenter: UIViewController, session: MXSession, userId: String, userDisplayName: String?) {
|
||||
self.presenter = presenter
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
self.present()
|
||||
}
|
||||
|
||||
func present() {
|
||||
let userVerificationCoordinator = UserVerificationCoordinator(presenter: self.presenter, session: self.session, userId: self.userId, userDisplayName: self.userDisplayName)
|
||||
userVerificationCoordinator.start()
|
||||
self.coordinator = userVerificationCoordinator
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
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 UserVerificationCoordinatorDelegate: class {
|
||||
func userVerificationCoordinatorDidComplete(_ coordinator: UserVerificationCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationCoordinatorType` is a protocol describing a Coordinator that handle user verification navigation flow.
|
||||
protocol UserVerificationCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationCoordinatorDelegate? { get }
|
||||
}
|
|
@ -24,10 +24,13 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
// MARK: Private
|
||||
|
||||
private var completions: [UIViewController : () -> Void]
|
||||
private let navigationController: UINavigationController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
private let navigationController: UINavigationController
|
||||
var modules: [Presentable] {
|
||||
return navigationController.viewControllers
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ protocol NavigationRouterType: class, Presentable {
|
|||
///
|
||||
/// - Parameter animated: Specify true to animate the transition.
|
||||
func popModule(animated: Bool)
|
||||
|
||||
/// Returns the modules that are currently in the navigation stack
|
||||
var modules: [Presentable] { get }
|
||||
}
|
||||
|
||||
// `NavigationRouterType` default implementation
|
||||
|
|
|
@ -17,3 +17,4 @@
|
|||
#import "AppDelegate.h"
|
||||
#import "RoomBubbleCellData.h"
|
||||
#import "MXKRoomBubbleTableViewCell+Riot.h"
|
||||
#import "UserEncryptionTrustLevel.h"
|
||||
|
|
Loading…
Reference in a new issue