Merge branch 'develop' into fix_join_room_no_guest_access
12
CHANGES.rst
|
@ -1,9 +1,19 @@
|
|||
Changes in 0.10.5 (TBD)
|
||||
Changes in 0.11.1 (TBD)
|
||||
===============================================
|
||||
|
||||
Bug fix:
|
||||
* Fix error when joining some public rooms. (#2888).
|
||||
|
||||
Changes in 0.11.0 (2020-xx-xx)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* E2E: Do not warn anymore for unknown devices
|
||||
* ON/OFF Cross-signing development in a Lab setting (#2855).
|
||||
* RoomVC: Update encryption decoration with shields (#2934, #2930, #2906).
|
||||
* Settings: Remove "End-to-End Encryption" from the LABS section (#2941).
|
||||
* Room decoration: Use shields instead of padlocks (#2906).
|
||||
|
||||
Changes in 0.10.4 (2019-12-11)
|
||||
===============================================
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@
|
|||
32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */; };
|
||||
32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; };
|
||||
32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; };
|
||||
3291DC8A23E0BE820009732F /* Security.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3291DC8923E0BE820009732F /* Security.storyboard */; };
|
||||
3291DC8D23E0BFF10009732F /* SecurityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291DC8C23E0BFF10009732F /* SecurityViewController.m */; };
|
||||
329E746622CD02EA006F9797 /* BubbleReactionActionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 329E746422CD02EA006F9797 /* BubbleReactionActionViewCell.xib */; };
|
||||
329E746722CD02EA006F9797 /* BubbleReactionActionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 329E746522CD02EA006F9797 /* BubbleReactionActionViewCell.swift */; };
|
||||
32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */; };
|
||||
|
@ -98,6 +100,8 @@
|
|||
32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; };
|
||||
32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */; };
|
||||
32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */; };
|
||||
32D5D16023E1EE2700E3E37C /* ManageSessionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D5D15D23E1EE2700E3E37C /* ManageSessionViewController.m */; };
|
||||
32D5D16123E1EE2700E3E37C /* ManageSession.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D15E23E1EE2700E3E37C /* ManageSession.storyboard */; };
|
||||
32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */; };
|
||||
32DB557622FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */; };
|
||||
32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */; };
|
||||
|
@ -129,6 +133,12 @@
|
|||
B105778B221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */; };
|
||||
B105778D2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105778C2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift */; };
|
||||
B105778F2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B105778E2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard */; };
|
||||
B108931F23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108931E23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift */; };
|
||||
B108932123AB8D7D00802670 /* KeyVerificationIncomingRequestApprovalViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108932023AB8D7D00802670 /* KeyVerificationIncomingRequestApprovalViewData.swift */; };
|
||||
B108932323AB908A00802670 /* KeyVerificationRequestStatusViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108932223AB908A00802670 /* KeyVerificationRequestStatusViewData.swift */; };
|
||||
B108932523AB93A200802670 /* KeyVerificationConclusionViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108932423AB93A200802670 /* KeyVerificationConclusionViewData.swift */; };
|
||||
B108932823ABEE6800802670 /* BubbleCellReadReceiptsDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108932723ABEE6700802670 /* BubbleCellReadReceiptsDisplayable.swift */; };
|
||||
B108932A23ACBA0B00802670 /* SizingViewHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108932923ACBA0B00802670 /* SizingViewHeight.swift */; };
|
||||
B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDA21ECE09E000DDA48 /* Strings.swift */; };
|
||||
B1098BE121ECE09F000DDA48 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDC21ECE09E000DDA48 /* Images.swift */; };
|
||||
B1098BE321ECE09F000DDA48 /* RiotDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDE21ECE09E000DDA48 /* RiotDefaults.swift */; };
|
||||
|
@ -169,12 +179,26 @@
|
|||
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 */; };
|
||||
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22021FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift */; };
|
||||
B139C22321FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22221FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift */; };
|
||||
B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22421FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift */; };
|
||||
B14084C623BF76890010F692 /* BubbleCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14084C523BF76890010F692 /* BubbleCellContentView.swift */; };
|
||||
B14084C823BF76CB0010F692 /* BubbleCellContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B14084C723BF76CB0010F692 /* BubbleCellContentView.xib */; };
|
||||
B14084CA23BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14084C923BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift */; };
|
||||
B14084CC23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14084CB23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift */; };
|
||||
B14084CE23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14084CD23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift */; };
|
||||
B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A121F87F7100E3F5FE /* OperationQueue.swift */; };
|
||||
B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */; };
|
||||
B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */; };
|
||||
|
@ -517,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 */; };
|
||||
|
@ -531,6 +576,11 @@
|
|||
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A81232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift */; };
|
||||
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A82232A8C2600165425 /* SettingsIdentityServerViewModel.swift */; };
|
||||
B1C45A8C232A8C2600165425 /* SettingsIdentityServerViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A83232A8C2600165425 /* SettingsIdentityServerViewAction.swift */; };
|
||||
B1C543A4239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C543A3239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift */; };
|
||||
B1C543A6239E999700DCA1FA /* KeyVerificationCellInnerContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C543A5239E999700DCA1FA /* KeyVerificationCellInnerContentView.xib */; };
|
||||
B1C543AE23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C543AD23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift */; };
|
||||
B1C543B023A2871300DCA1FA /* KeyVerificationBaseBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C543AF23A2871300DCA1FA /* KeyVerificationBaseBubbleCell.swift */; };
|
||||
B1C543B223A2913F00DCA1FA /* KeyVerificationConclusionBubbleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C543B123A2913F00DCA1FA /* KeyVerificationConclusionBubbleCell.swift */; };
|
||||
B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */; };
|
||||
B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562CB228AB3510037F12A /* UIStackView.swift */; };
|
||||
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */; };
|
||||
|
@ -730,6 +780,9 @@
|
|||
32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifiedViewController.swift; sourceTree = "<group>"; };
|
||||
32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = "<group>"; };
|
||||
32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = "<group>"; };
|
||||
3291DC8923E0BE820009732F /* Security.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Security.storyboard; sourceTree = "<group>"; };
|
||||
3291DC8B23E0BFF10009732F /* SecurityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecurityViewController.h; sourceTree = "<group>"; };
|
||||
3291DC8C23E0BFF10009732F /* SecurityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecurityViewController.m; sourceTree = "<group>"; };
|
||||
329E746422CD02EA006F9797 /* BubbleReactionActionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionActionViewCell.xib; sourceTree = "<group>"; };
|
||||
329E746522CD02EA006F9797 /* BubbleReactionActionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionActionViewCell.swift; sourceTree = "<group>"; };
|
||||
32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewState.swift; sourceTree = "<group>"; };
|
||||
|
@ -756,6 +809,9 @@
|
|||
32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewState.swift; sourceTree = "<group>"; };
|
||||
32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewAction.swift; sourceTree = "<group>"; };
|
||||
32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupTableViewSection.swift; sourceTree = "<group>"; };
|
||||
32D5D15D23E1EE2700E3E37C /* ManageSessionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ManageSessionViewController.m; sourceTree = "<group>"; };
|
||||
32D5D15E23E1EE2700E3E37C /* ManageSession.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ManageSession.storyboard; sourceTree = "<group>"; };
|
||||
32D5D15F23E1EE2700E3E37C /* ManageSessionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ManageSessionViewController.h; sourceTree = "<group>"; };
|
||||
32D7159E2146CC6F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Vector.strings; sourceTree = "<group>"; };
|
||||
32D7159F2146CC7F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
32D715A02146CC8800DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
|
@ -806,6 +862,12 @@
|
|||
B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupSuccessFromPassphraseViewController.storyboard; sourceTree = "<group>"; };
|
||||
B105778C2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupSuccessFromRecoveryKeyViewController.swift; sourceTree = "<group>"; };
|
||||
B105778E2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard; sourceTree = "<group>"; };
|
||||
B108931E23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationIncomingRequestApprovalBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B108932023AB8D7D00802670 /* KeyVerificationIncomingRequestApprovalViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationIncomingRequestApprovalViewData.swift; sourceTree = "<group>"; };
|
||||
B108932223AB908A00802670 /* KeyVerificationRequestStatusViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationRequestStatusViewData.swift; sourceTree = "<group>"; };
|
||||
B108932423AB93A200802670 /* KeyVerificationConclusionViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationConclusionViewData.swift; sourceTree = "<group>"; };
|
||||
B108932723ABEE6700802670 /* BubbleCellReadReceiptsDisplayable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BubbleCellReadReceiptsDisplayable.swift; sourceTree = "<group>"; };
|
||||
B108932923ACBA0B00802670 /* SizingViewHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizingViewHeight.swift; sourceTree = "<group>"; };
|
||||
B1098BDA21ECE09E000DDA48 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
B1098BDC21ECE09E000DDA48 /* Images.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = "<group>"; };
|
||||
B1098BDE21ECE09E000DDA48 /* RiotDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RiotDefaults.swift; sourceTree = "<group>"; };
|
||||
|
@ -846,12 +908,26 @@
|
|||
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>"; };
|
||||
B139C22021FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewState.swift; sourceTree = "<group>"; };
|
||||
B139C22221FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B139C22421FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseCoordinator.swift; sourceTree = "<group>"; };
|
||||
B14084C523BF76890010F692 /* BubbleCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BubbleCellContentView.swift; sourceTree = "<group>"; };
|
||||
B14084C723BF76CB0010F692 /* BubbleCellContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleCellContentView.xib; sourceTree = "<group>"; };
|
||||
B14084C923BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B14084CB23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationConclusionWithPaginationTitleBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B14084CD23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B140B4A121F87F7100E3F5FE /* OperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = "<group>"; };
|
||||
B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1379,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>"; };
|
||||
|
@ -1393,6 +1491,11 @@
|
|||
B1C45A81232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B1C45A82232A8C2600165425 /* SettingsIdentityServerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewModel.swift; sourceTree = "<group>"; };
|
||||
B1C45A83232A8C2600165425 /* SettingsIdentityServerViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewAction.swift; sourceTree = "<group>"; };
|
||||
B1C543A3239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationCellInnerContentView.swift; sourceTree = "<group>"; };
|
||||
B1C543A5239E999700DCA1FA /* KeyVerificationCellInnerContentView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KeyVerificationCellInnerContentView.xib; sourceTree = "<group>"; };
|
||||
B1C543AD23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationRequestStatusBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B1C543AF23A2871300DCA1FA /* KeyVerificationBaseBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationBaseBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B1C543B123A2913F00DCA1FA /* KeyVerificationConclusionBubbleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyVerificationConclusionBubbleCell.swift; sourceTree = "<group>"; };
|
||||
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
B1C562CB228AB3510037F12A /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
|
||||
B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuItem.swift; sourceTree = "<group>"; };
|
||||
|
@ -1648,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 */,
|
||||
|
@ -1771,6 +1875,17 @@
|
|||
path = Loading;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3291DC8823E0BE380009732F /* Security */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32D5D15C23E1EE2700E3E37C /* ManageSession */,
|
||||
3291DC8B23E0BFF10009732F /* SecurityViewController.h */,
|
||||
3291DC8C23E0BFF10009732F /* SecurityViewController.m */,
|
||||
3291DC8923E0BE820009732F /* Security.storyboard */,
|
||||
);
|
||||
path = Security;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32935CB21F628B98006888C8 /* js */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1828,6 +1943,16 @@
|
|||
path = KeyBackup;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32D5D15C23E1EE2700E3E37C /* ManageSession */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32D5D15D23E1EE2700E3E37C /* ManageSessionViewController.m */,
|
||||
32D5D15E23E1EE2700E3E37C /* ManageSession.storyboard */,
|
||||
32D5D15F23E1EE2700E3E37C /* ManageSessionViewController.h */,
|
||||
);
|
||||
path = ManageSession;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32DB556722FDADE50016329E /* ServiceTerms */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1911,6 +2036,16 @@
|
|||
path = Success;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B108932623ABE82C00802670 /* BaseContentViews */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B108932723ABEE6700802670 /* BubbleCellReadReceiptsDisplayable.swift */,
|
||||
B14084C523BF76890010F692 /* BubbleCellContentView.swift */,
|
||||
B14084C723BF76CB0010F692 /* BubbleCellContentView.xib */,
|
||||
);
|
||||
path = BaseContentViews;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1098BD921ECE09E000DDA48 /* Generated */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2051,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 = (
|
||||
|
@ -2395,6 +2545,33 @@
|
|||
path = Riot/Modules/Common/CollectionView;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
B1A12C64239AB74500AA2B86 /* CrossSigning */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = CrossSigning;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1A12C65239ABBDB00AA2B86 /* KeyVerification */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B108932923ACBA0B00802670 /* SizingViewHeight.swift */,
|
||||
B1C543A3239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift */,
|
||||
B1C543A5239E999700DCA1FA /* KeyVerificationCellInnerContentView.xib */,
|
||||
B1C543AF23A2871300DCA1FA /* KeyVerificationBaseBubbleCell.swift */,
|
||||
B108932023AB8D7D00802670 /* KeyVerificationIncomingRequestApprovalViewData.swift */,
|
||||
B108931E23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift */,
|
||||
B14084CD23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift */,
|
||||
B108932223AB908A00802670 /* KeyVerificationRequestStatusViewData.swift */,
|
||||
B1C543AD23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift */,
|
||||
B14084C923BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift */,
|
||||
B108932423AB93A200802670 /* KeyVerificationConclusionViewData.swift */,
|
||||
B1C543B123A2913F00DCA1FA /* KeyVerificationConclusionBubbleCell.swift */,
|
||||
B14084CB23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift */,
|
||||
);
|
||||
path = KeyVerification;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2413,6 +2590,8 @@
|
|||
B1B5567620EE6C4C00210D55 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE71023DF28CA0003A4CB /* UserVerification */,
|
||||
B1A12C64239AB74500AA2B86 /* CrossSigning */,
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */,
|
||||
32DB556722FDADE50016329E /* ServiceTerms */,
|
||||
3232AB94225730E100AD6A5C /* DeviceVerification */,
|
||||
|
@ -2457,6 +2636,7 @@
|
|||
B1B5567B20EE6C4C00210D55 /* Language */,
|
||||
B1B5567820EE6C4C00210D55 /* PhoneCountry */,
|
||||
B1B5568020EE6C4C00210D55 /* DeactivateAccount */,
|
||||
3291DC8823E0BE380009732F /* Security */,
|
||||
B1CE9EFB22148681000FAE6A /* SignOut */,
|
||||
);
|
||||
path = Settings;
|
||||
|
@ -2610,6 +2790,7 @@
|
|||
B1B556A620EE6C4C00210D55 /* Detail */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BEE73C23E070300003A4CB /* UserEncryptionTrustLevel.h */,
|
||||
B1B556A720EE6C4C00210D55 /* RoomMemberDetailsViewController.h */,
|
||||
B1B556A820EE6C4C00210D55 /* RoomMemberDetailsViewController.m */,
|
||||
B1B556A920EE6C4C00210D55 /* RoomMemberDetailsViewController.xib */,
|
||||
|
@ -3249,6 +3430,8 @@
|
|||
B1B5583F20EF768E00210D55 /* BubbleCells */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B108932623ABE82C00802670 /* BaseContentViews */,
|
||||
B1A12C65239ABBDB00AA2B86 /* KeyVerification */,
|
||||
B1B5584220EF768E00210D55 /* Encryption */,
|
||||
B1B5589220EF768E00210D55 /* RoomEmptyBubbleCell.h */,
|
||||
B1B558B620EF768E00210D55 /* RoomEmptyBubbleCell.m */,
|
||||
|
@ -3564,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 = (
|
||||
|
@ -4061,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 */,
|
||||
|
@ -4083,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 */,
|
||||
|
@ -4122,10 +4352,12 @@
|
|||
B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */,
|
||||
B1B5573320EE6C4D00210D55 /* GroupHomeViewController.xib in Resources */,
|
||||
3232AB2122564D9100AD6A5C /* README.md in Resources */,
|
||||
3291DC8A23E0BE820009732F /* Security.storyboard in Resources */,
|
||||
B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */,
|
||||
B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */,
|
||||
B1DCC61722E5E17100625807 /* EmojiPickerViewController.storyboard in Resources */,
|
||||
32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */,
|
||||
32D5D16123E1EE2700E3E37C /* ManageSession.storyboard in Resources */,
|
||||
B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */,
|
||||
B1664DA320F4F96200808783 /* Vector.strings in Resources */,
|
||||
B1B557C720EF5CD400210D55 /* DirectoryServerDetailTableViewCell.xib in Resources */,
|
||||
|
@ -4150,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 */,
|
||||
|
@ -4176,6 +4410,7 @@
|
|||
B1B558FB20EF768F00210D55 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1B558BB20EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1B557A720EF5A1B00210D55 /* DeviceTableViewCell.xib in Resources */,
|
||||
B14084C823BF76CB0010F692 /* BubbleCellContentView.xib in Resources */,
|
||||
B1B557D220EF5E3500210D55 /* MediaAlbumTableCell.xib in Resources */,
|
||||
B1B558C520EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.xib in Resources */,
|
||||
B1B5582B20EF666100210D55 /* DirectoryRecentTableViewCell.xib in Resources */,
|
||||
|
@ -4198,6 +4433,7 @@
|
|||
B1B557A220EF58AD00210D55 /* ContactTableViewCell.xib in Resources */,
|
||||
B1B558EB20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B10B3B5C2201DD740072C76B /* KeyBackupBannerCell.xib in Resources */,
|
||||
B1C543A6239E999700DCA1FA /* KeyVerificationCellInnerContentView.xib in Resources */,
|
||||
32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */,
|
||||
B1B5581820EF625800210D55 /* PreviewRoomTitleView.xib in Resources */,
|
||||
B1B5583020EF66BA00210D55 /* RoomIdOrAliasTableViewCell.xib in Resources */,
|
||||
|
@ -4441,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 */,
|
||||
|
@ -4448,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 */,
|
||||
|
@ -4456,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 */,
|
||||
|
@ -4469,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 */,
|
||||
|
@ -4481,6 +4721,7 @@
|
|||
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
|
||||
B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */,
|
||||
B108931F23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift in Sources */,
|
||||
B1B5599220EFC5E400210D55 /* Analytics.m in Sources */,
|
||||
B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */,
|
||||
B1098BF621ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift in Sources */,
|
||||
|
@ -4496,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 */,
|
||||
|
@ -4503,6 +4745,7 @@
|
|||
B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */,
|
||||
32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */,
|
||||
3291DC8D23E0BFF10009732F /* SecurityViewController.m in Sources */,
|
||||
B1C45A88232A8C2600165425 /* SettingsIdentityServerViewState.swift in Sources */,
|
||||
B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */,
|
||||
32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */,
|
||||
|
@ -4520,7 +4763,9 @@
|
|||
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 */,
|
||||
B1B558E920EF768F00210D55 /* RoomSelectedStickerBubbleCell.m in Sources */,
|
||||
B1C3360222F1ED600021BA8D /* MediaPickerCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -4535,6 +4780,7 @@
|
|||
3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */,
|
||||
B1B5572720EE6C4D00210D55 /* RoomSearchViewController.m in Sources */,
|
||||
3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */,
|
||||
B14084C623BF76890010F692 /* BubbleCellContentView.swift in Sources */,
|
||||
F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */,
|
||||
B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */,
|
||||
B1A6C109238828A6002882FD /* SlidingModalPresentationDelegate.swift in Sources */,
|
||||
|
@ -4570,11 +4816,13 @@
|
|||
32242F1421E8FBA900725742 /* DefaultTheme.swift in Sources */,
|
||||
B1963B2D228F1C4900CBA17F /* BubbleReactionsViewModel.swift in Sources */,
|
||||
32242F1321E8FBA900725742 /* Theme.swift in Sources */,
|
||||
B108932523AB93A200802670 /* KeyVerificationConclusionViewData.swift in Sources */,
|
||||
B1B5582520EF638A00210D55 /* RoomMemberTitleView.m in Sources */,
|
||||
B1B5582C20EF666100210D55 /* DirectoryRecentTableViewCell.m in Sources */,
|
||||
B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */,
|
||||
B125FE21231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift in Sources */,
|
||||
B108932323AB908A00802670 /* KeyVerificationRequestStatusViewData.swift in Sources */,
|
||||
3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */,
|
||||
B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */,
|
||||
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */,
|
||||
|
@ -4588,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 */,
|
||||
|
@ -4599,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 */,
|
||||
|
@ -4608,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 */,
|
||||
|
@ -4619,6 +4873,8 @@
|
|||
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 */,
|
||||
B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */,
|
||||
|
@ -4629,11 +4885,13 @@
|
|||
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 */,
|
||||
B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */,
|
||||
B125FE1F231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift in Sources */,
|
||||
B108932A23ACBA0B00802670 /* SizingViewHeight.swift in Sources */,
|
||||
B157FAA323264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift in Sources */,
|
||||
B1098BF921ECFE65000DDA48 /* KeyBackupSetupCoordinator.swift in Sources */,
|
||||
B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -4650,17 +4908,22 @@
|
|||
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 */,
|
||||
B1B5578F20EF568D00210D55 /* GroupTableViewCell.m in Sources */,
|
||||
32D5D16023E1EE2700E3E37C /* ManageSessionViewController.m in Sources */,
|
||||
B1B5573220EE6C4D00210D55 /* GroupHomeViewController.m in Sources */,
|
||||
B1B5595220EF9A8700210D55 /* RecentTableViewCell.m in Sources */,
|
||||
32F6B96C2270623100BBA352 /* DeviceVerificationDataLoadingCoordinatorType.swift in Sources */,
|
||||
B14084CA23BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift in Sources */,
|
||||
B1DCC61D22E5E17100625807 /* EmojiPickerViewModelType.swift in Sources */,
|
||||
B1B5574120EE6C4D00210D55 /* RecentsViewController.m in Sources */,
|
||||
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 */,
|
||||
|
@ -4675,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 */,
|
||||
|
@ -4689,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 */,
|
||||
|
@ -4705,10 +4971,12 @@
|
|||
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 */,
|
||||
B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */,
|
||||
B108932123AB8D7D00802670 /* KeyVerificationIncomingRequestApprovalViewData.swift in Sources */,
|
||||
B1B9DEED22EB34EF0065E677 /* ReactionHistoryCoordinator.swift in Sources */,
|
||||
B1DCC62A22E60D1000625807 /* EmojiMartService.swift in Sources */,
|
||||
B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
|
@ -4717,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 */,
|
||||
|
@ -4734,6 +5003,7 @@
|
|||
B157FAA223264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift in Sources */,
|
||||
3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */,
|
||||
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */,
|
||||
B1C543B223A2913F00DCA1FA /* KeyVerificationConclusionBubbleCell.swift in Sources */,
|
||||
323AB947232BD74600C1451F /* AuthFallBackViewController.m in Sources */,
|
||||
B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */,
|
||||
B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */,
|
||||
|
@ -4745,12 +5015,14 @@
|
|||
B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */,
|
||||
B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */,
|
||||
B139C22321FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift in Sources */,
|
||||
B14084CE23BFA0990010F692 /* KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift in Sources */,
|
||||
B14F143222144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift in Sources */,
|
||||
B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */,
|
||||
B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */,
|
||||
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 */,
|
||||
|
@ -4789,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 */,
|
||||
|
@ -4798,16 +5071,21 @@
|
|||
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 */,
|
||||
B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */,
|
||||
B1DB4F06223015080065DBFA /* Character.swift in Sources */,
|
||||
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 */,
|
||||
|
|
|
@ -130,6 +130,17 @@ extern NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey;
|
|||
- (void)logoutSendingRequestServer:(BOOL)sendLogoutServerRequest
|
||||
completion:(void (^)(BOOL isLoggedOut))completion;
|
||||
|
||||
/**
|
||||
Present incoming key verification request to accept.
|
||||
|
||||
@param incomingKeyVerificationRequest The incoming key verification request.
|
||||
@param The matrix session.
|
||||
@return Indicate NO if the key verification screen could not be presented.
|
||||
*/
|
||||
- (BOOL)presentIncomingKeyVerificationRequest:(MXKeyVerificationRequest*)incomingKeyVerificationRequest
|
||||
inSession:(MXSession*)session;
|
||||
|
||||
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession;
|
||||
|
||||
#pragma mark - Matrix Accounts handling
|
||||
|
||||
|
|
|
@ -237,6 +237,8 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
@property (weak, nonatomic) UIAlertController *gdprConsentNotGivenAlertController;
|
||||
@property (weak, nonatomic) UIViewController *gdprConsentController;
|
||||
|
||||
@property (weak, nonatomic) UIAlertController *incomingKeyVerificationRequestAlertController;
|
||||
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) SlidingModalPresenter *slidingModalPresenter;
|
||||
|
||||
|
@ -264,6 +266,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
// Set the App Group identifier.
|
||||
MXSDKOptions *sdkOptions = [MXSDKOptions sharedInstance];
|
||||
sdkOptions.applicationGroupIdentifier = @"group.im.vector";
|
||||
sdkOptions.computeE2ERoomSummaryTrust = YES;
|
||||
|
||||
// Redirect NSLogs to files only if we are not debugging
|
||||
if (!isatty(STDERR_FILENO))
|
||||
|
@ -695,6 +698,9 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
// Observe wrong backup version
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBackupStateDidChangeNotification:) name:kMXKeyBackupDidStateChangeNotification object:nil];
|
||||
|
||||
// Observe key verification request
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyVerificationRequestDidChangeNotification:) name:MXDeviceVerificationManagerNewRequestNotification object:nil];
|
||||
|
||||
// Resume all existing matrix sessions
|
||||
NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
|
||||
for (MXKAccount *account in mxAccounts)
|
||||
|
@ -713,6 +719,85 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
[self handleLaunchAnimation];
|
||||
}
|
||||
|
||||
- (void)keyVerificationRequestDidChangeNotification:(NSNotification *)notification
|
||||
{
|
||||
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *userInfo = notification.userInfo;
|
||||
|
||||
MXKeyVerificationRequest *keyVerificationRequest = userInfo[MXDeviceVerificationManagerNotificationRequestKey];
|
||||
|
||||
if ([keyVerificationRequest isKindOfClass:MXKeyVerificationByDMRequest.class])
|
||||
{
|
||||
MXKeyVerificationByDMRequest *keyVerificationByDMRequest = (MXKeyVerificationByDMRequest*)keyVerificationRequest;
|
||||
|
||||
if (!keyVerificationByDMRequest.isFromMyUser && keyVerificationByDMRequest.state == MXKeyVerificationRequestStatePending)
|
||||
{
|
||||
MXKAccount *currentAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
MXRoom *room = [currentAccount.mxSession roomWithRoomId:keyVerificationByDMRequest.roomId];
|
||||
if (!room)
|
||||
{
|
||||
NSLog(@"[AppDelegate][KeyVerification] keyVerificationRequestDidChangeNotification: Unknown room");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *sender = keyVerificationByDMRequest.sender;
|
||||
|
||||
[room state:^(MXRoomState *roomState) {
|
||||
|
||||
NSString *senderName = [roomState.members memberName:sender];
|
||||
|
||||
if (self.incomingKeyVerificationRequestAlertController)
|
||||
{
|
||||
[self.incomingKeyVerificationRequestAlertController dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
NSMutableString *senderInfo = [NSMutableString stringWithString:sender];
|
||||
|
||||
if (senderName)
|
||||
{
|
||||
[senderInfo appendFormat:@" (%@)", senderName];
|
||||
}
|
||||
|
||||
NSString *alertMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"key_verification_incoming_request_incoming_alert_message", @"Vector", nil), senderInfo];
|
||||
|
||||
self.incomingKeyVerificationRequestAlertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_title", @"Vector", nil)
|
||||
message:alertMessage
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_accept", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[self presentIncomingKeyVerificationRequest:keyVerificationByDMRequest inSession:self.mxSessions.firstObject];
|
||||
}]];
|
||||
|
||||
[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_decline", @"Vector", nil)
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[keyVerificationByDMRequest cancelWithCancelCode:MXTransactionCancelCode.user success:^{
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
NSLog(@"[AppDelegate][KeyVerification] Fail to cancel incoming key verification request with error: %@", error);
|
||||
}];
|
||||
}]];
|
||||
|
||||
[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"later", @"Vector", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
}]];
|
||||
|
||||
[self showNotificationAlert:self.incomingKeyVerificationRequestAlertController];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application
|
||||
{
|
||||
NSLog(@"[AppDelegate] applicationWillTerminate");
|
||||
|
@ -1600,132 +1685,6 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
return notificationUserInfo;
|
||||
}
|
||||
|
||||
- (void)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account onComplete:(void (^)(NSString * _Nullable notificationBody))onComplete;
|
||||
{
|
||||
if (!event.content || !event.content.count)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: empty event content");
|
||||
onComplete (nil);
|
||||
return;
|
||||
}
|
||||
|
||||
MXRoom *room = [account.mxSession roomWithRoomId:event.roomId];
|
||||
if (!room)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Unknown room");
|
||||
onComplete (nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[room state:^(MXRoomState *roomState) {
|
||||
|
||||
NSString *notificationBody;
|
||||
NSString *eventSenderName = [roomState.members memberName:event.sender];
|
||||
|
||||
if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted)
|
||||
{
|
||||
if (room.isMentionsOnly)
|
||||
{
|
||||
// A local notification will be displayed only for highlighted notification.
|
||||
BOOL isHighlighted = NO;
|
||||
|
||||
// Check whether is there an highlight tweak on it
|
||||
for (MXPushRuleAction *ruleAction in rule.actions)
|
||||
{
|
||||
if (ruleAction.actionType == MXPushRuleActionTypeSetTweak)
|
||||
{
|
||||
if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"])
|
||||
{
|
||||
// Check the highlight tweak "value"
|
||||
// If not present, highlight. Else check its value before highlighting
|
||||
if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue])
|
||||
{
|
||||
isHighlighted = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHighlighted)
|
||||
{
|
||||
// Ignore this notif.
|
||||
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room");
|
||||
onComplete(nil);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *msgType = event.content[@"msgtype"];
|
||||
NSString *content = event.content[@"body"];
|
||||
|
||||
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
|
||||
{
|
||||
// Hide the content
|
||||
msgType = nil;
|
||||
}
|
||||
|
||||
NSString *roomDisplayName = room.summary.displayname;
|
||||
|
||||
// Display the room name only if it is different than the sender name
|
||||
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
|
||||
{
|
||||
if ([msgType isEqualToString:@"m.text"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content];
|
||||
else if ([msgType isEqualToString:@"m.emote"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content];
|
||||
else if ([msgType isEqualToString:@"m.image"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName];
|
||||
else
|
||||
// Encrypted messages falls here
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([msgType isEqualToString:@"m.text"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content];
|
||||
else if ([msgType isEqualToString:@"m.emote"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content];
|
||||
else if ([msgType isEqualToString:@"m.image"])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content];
|
||||
else
|
||||
// Encrypted messages falls here
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
|
||||
}
|
||||
}
|
||||
else if (event.eventType == MXEventTypeCallInvite)
|
||||
{
|
||||
NSString *sdp = event.content[@"offer"][@"sdp"];
|
||||
BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound;
|
||||
|
||||
if (!isVideoCall)
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName];
|
||||
else
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName];
|
||||
}
|
||||
else if (event.eventType == MXEventTypeRoomMember)
|
||||
{
|
||||
NSString *roomDisplayName = room.summary.displayname;
|
||||
|
||||
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName];
|
||||
else
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName];
|
||||
}
|
||||
else if (event.eventType == MXEventTypeSticker)
|
||||
{
|
||||
NSString *roomDisplayName = room.summary.displayname;
|
||||
|
||||
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
|
||||
else
|
||||
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
|
||||
}
|
||||
|
||||
onComplete(notificationBody);
|
||||
}];
|
||||
}
|
||||
|
||||
// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features
|
||||
- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNNotificationContent * _Nullable notificationContent))onComplete;
|
||||
{
|
||||
|
@ -1751,6 +1710,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
|
||||
NSString *threadIdentifier = room.roomId;
|
||||
NSString *eventSenderName = [roomState.members memberName:event.sender];
|
||||
NSString *currentUserId = account.mxCredentials.userId;
|
||||
|
||||
if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted)
|
||||
{
|
||||
|
@ -1797,6 +1757,9 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
|
||||
NSString *roomDisplayName = room.summary.displayname;
|
||||
|
||||
NSString *myUserId = account.mxSession.myUser.userId;
|
||||
BOOL isIncomingEvent = ![event.sender isEqualToString:myUserId];
|
||||
|
||||
// Display the room name only if it is different than the sender name
|
||||
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
|
||||
{
|
||||
|
@ -1814,6 +1777,30 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
{
|
||||
notificationBody = [NSString localizedUserNotificationStringForKey:@"IMAGE_FROM_USER" arguments:@[eventSenderName, messageContent]];
|
||||
}
|
||||
else if (room.isDirect && isIncomingEvent && [msgType isEqualToString:kMXMessageTypeKeyVerificationRequest])
|
||||
{
|
||||
[account.mxSession.crypto.deviceVerificationManager keyVerificationFromKeyVerificationEvent:event
|
||||
success:^(MXKeyVerification * _Nonnull keyVerification)
|
||||
{
|
||||
if (keyVerification && keyVerification.state == MXKeyVerificationRequestStatePending)
|
||||
{
|
||||
// TODO: Add accept and decline actions to notification
|
||||
NSString *body = [NSString localizedUserNotificationStringForKey:@"KEY_VERIFICATION_REQUEST_FROM_USER" arguments:@[eventSenderName]];
|
||||
|
||||
UNNotificationContent *notificationContent = [self notificationContentWithTitle:notificationTitle
|
||||
body:body
|
||||
threadIdentifier:threadIdentifier
|
||||
userId:currentUserId
|
||||
event:event
|
||||
pushRule:rule];
|
||||
|
||||
onComplete(notificationContent);
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
NSLog(@"[AppDelegate][Push] notificationContentForEvent: failed to fetch key verification with error: %@", error);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encrypted messages falls here
|
||||
|
@ -1889,27 +1876,47 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
notificationBody = [NSString localizedUserNotificationStringForKey:@"STICKER_FROM_USER" arguments:@[eventSenderName]];
|
||||
}
|
||||
|
||||
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
|
||||
|
||||
NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:account.mxCredentials.userId];
|
||||
NSString *notificationSoundName = [self notificationSoundNameFromPushRule:rule];
|
||||
NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event];
|
||||
|
||||
notificationContent.title = notificationTitle;
|
||||
notificationContent.body = notificationBody;
|
||||
notificationContent.threadIdentifier = threadIdentifier;
|
||||
notificationContent.userInfo = notificationUserInfo;
|
||||
notificationContent.categoryIdentifier = categoryIdentifier;
|
||||
|
||||
if (notificationSoundName)
|
||||
if (notificationBody)
|
||||
{
|
||||
notificationContent.sound = [UNNotificationSound soundNamed:notificationSoundName];
|
||||
}
|
||||
UNNotificationContent *notificationContent = [self notificationContentWithTitle:notificationTitle
|
||||
body:notificationBody
|
||||
threadIdentifier:threadIdentifier
|
||||
userId:currentUserId
|
||||
event:event
|
||||
pushRule:rule];
|
||||
|
||||
onComplete([notificationContent copy]);
|
||||
onComplete(notificationContent);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (UNNotificationContent*)notificationContentWithTitle:(NSString*)title
|
||||
body:(NSString*)body
|
||||
threadIdentifier:(NSString*)threadIdentifier
|
||||
userId:(NSString*)userId
|
||||
event:(MXEvent*)event
|
||||
pushRule:(MXPushRule*)pushRule
|
||||
{
|
||||
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
|
||||
|
||||
NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:userId];
|
||||
NSString *notificationSoundName = [self notificationSoundNameFromPushRule:pushRule];
|
||||
NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event];
|
||||
|
||||
notificationContent.title = title;
|
||||
notificationContent.body = body;
|
||||
notificationContent.threadIdentifier = threadIdentifier;
|
||||
notificationContent.userInfo = notificationUserInfo;
|
||||
notificationContent.categoryIdentifier = categoryIdentifier;
|
||||
|
||||
if (notificationSoundName)
|
||||
{
|
||||
notificationContent.sound = [UNNotificationSound soundNamed:notificationSoundName];
|
||||
}
|
||||
|
||||
return [notificationContent copy];
|
||||
}
|
||||
|
||||
/**
|
||||
Display "limited" notifications for events the app was not able to get data
|
||||
(because of /sync failure).
|
||||
|
@ -2795,6 +2802,9 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not warn for unknown devices if cross-signing is enabled
|
||||
mxSession.crypto.warnOnUnknowDevices = !RiotSettings.shared.enableCrossSigning;
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateClosed)
|
||||
{
|
||||
|
@ -3835,37 +3845,45 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
// Create a new room by inviting the other user only if it is defined and not oneself
|
||||
NSArray *invite = ((userId && ![mxSession.myUser.userId isEqualToString:userId]) ? @[userId] : nil);
|
||||
|
||||
[mxSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
invite:invite
|
||||
invite3PID:nil
|
||||
isDirect:(invite.count != 0)
|
||||
preset:kMXRoomPresetTrustedPrivateChat
|
||||
success:^(MXRoom *room) {
|
||||
void (^onFailure)(NSError *) = ^(NSError *error){
|
||||
NSLog(@"[AppDelegate] Create direct chat failed");
|
||||
//Alert user
|
||||
[self showErrorAsAlert:error];
|
||||
|
||||
// Open created room
|
||||
[self showRoom:room.roomId andEventId:nil withMatrixSession:mxSession];
|
||||
if (completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
};
|
||||
|
||||
if (completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
[mxSession canEnableE2EByDefaultInNewRoomWithUsers:invite success:^(BOOL canEnableE2E) {
|
||||
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new];
|
||||
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate;
|
||||
roomCreationParameters.inviteArray = invite;
|
||||
roomCreationParameters.isDirect = (invite.count != 0);
|
||||
roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat;
|
||||
|
||||
NSLog(@"[AppDelegate] Create direct chat failed");
|
||||
//Alert user
|
||||
[self showErrorAsAlert:error];
|
||||
if (canEnableE2E)
|
||||
{
|
||||
roomCreationParameters.initialStateEvents = @[
|
||||
[MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm
|
||||
]];
|
||||
}
|
||||
|
||||
if (completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
[mxSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
|
||||
|
||||
}];
|
||||
// Open created room
|
||||
[self showRoom:room.roomId andEventId:nil withMatrixSession:mxSession];
|
||||
|
||||
if (completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
|
||||
} failure:onFailure];
|
||||
|
||||
} failure:onFailure];
|
||||
}
|
||||
else if (completion)
|
||||
{
|
||||
|
@ -4701,12 +4719,12 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
NSString *deviceId = [pendingKeyRequests deviceIdsForUser:userId].firstObject;
|
||||
|
||||
// Give the client a chance to refresh the device list
|
||||
[mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap) {
|
||||
[mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:deviceId forUser:userId];
|
||||
if (deviceInfo)
|
||||
{
|
||||
BOOL wasNewDevice = (deviceInfo.verified == MXDeviceUnknown);
|
||||
BOOL wasNewDevice = (deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown);
|
||||
|
||||
void (^openDialog)(void) = ^void()
|
||||
{
|
||||
|
@ -4826,6 +4844,32 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL)presentIncomingKeyVerificationRequest:(MXKeyVerificationRequest*)incomingKeyVerificationRequest
|
||||
inSession:(MXSession*)session
|
||||
{
|
||||
BOOL presented = NO;
|
||||
|
||||
if (!deviceVerificationCoordinatorBridgePresenter)
|
||||
{
|
||||
NSLog(@"[AppDelegate] presentIncomingKeyVerificationRequest");
|
||||
|
||||
UIViewController *presentingViewController = self.window.rootViewController.presentedViewController ?: self.window.rootViewController;
|
||||
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:session];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:presentingViewController incomingKeyVerificationRequest:incomingKeyVerificationRequest animated:YES];
|
||||
|
||||
presented = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[AppDelegate][MXKeyVerification] presentIncomingDeviceVerification: Controller already presented.");
|
||||
}
|
||||
|
||||
return presented;
|
||||
}
|
||||
|
||||
- (BOOL)presentIncomingDeviceVerification:(MXIncomingSASTransaction*)transaction inSession:(MXSession*)mxSession
|
||||
{
|
||||
NSLog(@"[AppDelegate][MXKeyVerification] presentIncomingDeviceVerification: %@", transaction);
|
||||
|
@ -4849,6 +4893,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:^{
|
||||
|
|
6
Riot/Assets/Images.xcassets/Encryption/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
23
Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_normal.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_normal@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_normal@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal.png
vendored
Normal file
After Width: | Height: | Size: 384 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@2x.png
vendored
Normal file
After Width: | Height: | Size: 627 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_normal.imageset/encryption_normal@3x.png
vendored
Normal file
After Width: | Height: | Size: 913 B |
23
Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_trusted.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_trusted@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_trusted@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted.png
vendored
Normal file
After Width: | Height: | Size: 476 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@2x.png
vendored
Normal file
After Width: | Height: | Size: 804 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_trusted.imageset/encryption_trusted@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
23
Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_warning.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_warning@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "encryption_warning@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning.png
vendored
Normal file
After Width: | Height: | Size: 423 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@2x.png
vendored
Normal file
After Width: | Height: | Size: 688 B |
BIN
Riot/Assets/Images.xcassets/Encryption/encryption_warning.imageset/encryption_warning@3x.png
vendored
Normal file
After Width: | Height: | Size: 980 B |
|
@ -109,3 +109,7 @@
|
|||
|
||||
/* Incoming named video conference invite from a specific person */
|
||||
"VIDEO_CONF_NAMED_FROM_USER" = "Video group call from %@: '%@'";
|
||||
|
||||
/** Key verification **/
|
||||
|
||||
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ wants to verify";
|
||||
|
|
|
@ -240,7 +240,8 @@
|
|||
"room_participants_action_section_admin_tools" = "Admin tools";
|
||||
"room_participants_action_section_direct_chats" = "Direct chats";
|
||||
"room_participants_action_section_devices" = "Devices";
|
||||
"room_participants_action_section_other" = "Other";
|
||||
"room_participants_action_section_other" = "Options";
|
||||
"room_participants_action_section_security" = "Security";
|
||||
|
||||
"room_participants_action_invite" = "Invite";
|
||||
"room_participants_action_leave" = "Leave this room";
|
||||
|
@ -256,6 +257,13 @@
|
|||
"room_participants_action_start_voice_call" = "Start voice call";
|
||||
"room_participants_action_start_video_call" = "Start video call";
|
||||
"room_participants_action_mention" = "Mention";
|
||||
"room_participants_action_security_status_verified" = "Verified";
|
||||
"room_participants_action_security_status_verify" = "Verify";
|
||||
"room_participants_action_security_status_warning" = "Warning";
|
||||
|
||||
"room_participants_security_loading" = "Loading…";
|
||||
"room_participants_security_information_room_not_encrypted" = "Messages in this room are not end-to-end encrypted.";
|
||||
"room_participants_security_information_room_encrypted" = "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.";
|
||||
|
||||
// Chat
|
||||
"room_jump_to_first_unread" = "Jump to first unread message";
|
||||
|
@ -416,6 +424,8 @@
|
|||
"settings_three_pids_management_information_part2" = "Discovery";
|
||||
"settings_three_pids_management_information_part3" = ".";
|
||||
|
||||
"settings_security" = "SECURITY";
|
||||
|
||||
"settings_enable_push_notif" = "Notifications on this device";
|
||||
"settings_show_decrypted_content" = "Show decrypted content";
|
||||
"settings_global_settings_info" = "Global notification settings are available on your %@ web client";
|
||||
|
@ -458,8 +468,7 @@
|
|||
"settings_labs_room_members_lazy_loading_error_message" = "Your homeserver does not support lazy loading of room members yet. Try later.";
|
||||
"settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi";
|
||||
"settings_labs_message_reaction" = "React to messages with emoji";
|
||||
"settings_labs_dm_key_verification" = "Key verification by direct message";
|
||||
"settings_labs_cross_signing" = "Cross-Signing";
|
||||
"settings_labs_enable_cross_signing" = "Enable cross-signing to verify per-user instead of per-device (in development)";
|
||||
|
||||
"settings_version" = "Version %@";
|
||||
"settings_olm_version" = "Olm Version %@";
|
||||
|
@ -543,6 +552,28 @@
|
|||
"settings_identity_server_no_is_description" = "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one above.";
|
||||
|
||||
|
||||
// Security settings
|
||||
"security_settings_title" = "Security";
|
||||
"security_settings_crypto_sessions" = "MY SESSIONS";
|
||||
"security_settings_crypto_sessions_description" = "Trust sessions to grant access to end-to-end encrypted messages. If you don’t recognise a session, change your login password and reset your Message Password used for Message Backup.";
|
||||
|
||||
"security_settings_backup" = "MESSAGE BACKUP";
|
||||
|
||||
"security_settings_advanced" = "ADVANCED";
|
||||
"security_settings_blacklist_unverified_devices" = "Never send messages to untrusted sessions";
|
||||
"security_settings_blacklist_unverified_devices_description" = "Verify all of a users sessions to mark them as trusted and send messages to them.";
|
||||
"security_settings_export_keys_manually" = "Export keys manually";
|
||||
|
||||
|
||||
// Manage session
|
||||
"manage_session_title" = "Manage session";
|
||||
"manage_session_info" = "SESSION INFO";
|
||||
"manage_session_name" = "Device name";
|
||||
"manage_session_trusted" = "Trusted by you";
|
||||
"manage_session_not_trusted" = "Not trusted";
|
||||
"manage_session_sign_out" = "Sign out of this device";
|
||||
|
||||
|
||||
// Identity server settings
|
||||
"identity_server_settings_title" = "Identity Server";
|
||||
|
||||
|
@ -970,6 +1001,8 @@
|
|||
|
||||
// MARK: - Device Verification
|
||||
"device_verification_title" = "Verify device";
|
||||
"key_verification_user_title" = "Verify user";
|
||||
|
||||
"device_verification_security_advice" = "For maximum security, we recommend you do this in person or use another trusted means of communication";
|
||||
"device_verification_cancelled" = "The other party cancelled the verification.";
|
||||
"device_verification_cancelled_by_me" = "The verification has been cancelled. Reason: %@";
|
||||
|
@ -988,16 +1021,32 @@
|
|||
"device_verification_start_use_legacy_action" = "Use Legacy Verification";
|
||||
|
||||
// MARK: Verify
|
||||
|
||||
// Device
|
||||
|
||||
"device_verification_verify_title_emoji" = "Verify this device by confirming the following emoji appear on the screen of the partner";
|
||||
"device_verification_verify_title_number" = "Verify this device by confirming the following numbers appear on the screen of the partner";
|
||||
"device_verification_verify_wait_partner" = "Waiting for partner to confirm...";
|
||||
|
||||
// User
|
||||
|
||||
"key_verification_verify_user_title_emoji" = "Verify this user by confirming the following unique emoji appears on their screen, in the same order.";
|
||||
"key_verification_verify_user_title_number" = "Verify this user by confirming the following numbers appear on their screen, in the same order.";
|
||||
|
||||
// MARK: Verified
|
||||
|
||||
// Device
|
||||
|
||||
"device_verification_verified_title" = "Verified!";
|
||||
"device_verification_verified_description_1" = "You've successfully verified this device.";
|
||||
"device_verification_verified_description_2" = "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.";
|
||||
"device_verification_verified_got_it_button" = "Got it";
|
||||
|
||||
// User
|
||||
|
||||
"key_verification_verified_user_description_1" = "You’ve successfully verified this user.";
|
||||
"key_verification_verified_user_description_2" = "Messages with this user in this room are end-to-end encrypted and can’t be read by third parties.";
|
||||
|
||||
// MARK: Emoji
|
||||
"device_verification_emoji_dog" = "Dog";
|
||||
"device_verification_emoji_cat" = "Cat";
|
||||
|
@ -1087,3 +1136,65 @@
|
|||
// Generic errors
|
||||
"error_invite_3pid_with_no_identity_server" = "Add an identity server in your settings to invite by email.";
|
||||
"error_not_supported_on_mobile" = "You can't do this from %@ mobile.";
|
||||
|
||||
// MARK: - Key Verification
|
||||
|
||||
// Tiles
|
||||
|
||||
"key_verification_tile_request_incoming_title" = "Verification request";
|
||||
"key_verification_tile_request_outgoing_title" = "Verification sent";
|
||||
|
||||
"key_verification_tile_request_status_data_loading" = "Data loading…";
|
||||
"key_verification_tile_request_status_waiting" = "Waiting…";
|
||||
"key_verification_tile_request_status_expired" = "Expired";
|
||||
"key_verification_tile_request_status_cancelled_by_me" = "You cancelled";
|
||||
"key_verification_tile_request_status_cancelled" = "%@ cancelled";
|
||||
"key_verification_tile_request_status_accepted" = "You accepted";
|
||||
|
||||
"key_verification_tile_request_incoming_approval_accept" = "Accept";
|
||||
"key_verification_tile_request_incoming_approval_decline" = "Decline";
|
||||
|
||||
"key_verification_tile_conclusion_done_title" = "Verified";
|
||||
"key_verification_tile_conclusion_warning_title" = "Unstrusted sign in";
|
||||
|
||||
// Incoming key verification request
|
||||
|
||||
"key_verification_incoming_request_incoming_alert_message" = "%@ wants to verify";
|
||||
|
||||
// MARK: - User verification
|
||||
|
||||
// Start
|
||||
|
||||
"user_verification_start_verify_action" = "Start verification";
|
||||
"user_verification_start_information_part1" = "For extra security, verify ";
|
||||
"user_verification_start_information_part2" = " by checking a one-time code on both your devices.";
|
||||
"user_verification_start_waiting_partner" = "Waiting for %@…";
|
||||
"user_verification_start_additional_information" = "To be secure, do this in person or use another way to communicate.";
|
||||
|
||||
// Sessions list
|
||||
|
||||
"user_verification_sessions_list_user_trust_level_trusted_title" = "Trusted";
|
||||
"user_verification_sessions_list_user_trust_level_warning_title" = "Warning";
|
||||
"user_verification_sessions_list_user_trust_level_unknown_title" = "Unknown";
|
||||
"user_verification_sessions_list_information" = "Messages with this user in this room are end-to-end encrypted and can’t be read by third parties.";
|
||||
"user_verification_sessions_list_table_title" = "Sessions";
|
||||
"user_verification_sessions_list_session_trusted" = "Trusted";
|
||||
"user_verification_sessions_list_session_untrusted" = "Not trusted";
|
||||
|
||||
// Session details
|
||||
|
||||
"user_verification_session_details_trusted_title" = "Trusted";
|
||||
"user_verification_session_details_untrusted_title" = "Warning";
|
||||
|
||||
"user_verification_session_details_information_trusted_current_user" = "This session is trusted for secure messaging because you verified it:";
|
||||
"user_verification_session_details_information_trusted_other_user_part1" = "This device is trusted for secure messaging because ";
|
||||
"user_verification_session_details_information_trusted_other_user_part2" = " verified it:";
|
||||
|
||||
"user_verification_session_details_information_untrusted_current_user" = "Verify this session to mark it as trusted & grant it access to encrypted messages:";
|
||||
"user_verification_session_details_information_untrusted_other_user" = " signed in using a new device:";
|
||||
|
||||
"user_verification_session_details_additional_information_untrusted_other_user" = "Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it.";
|
||||
"user_verification_session_details_additional_information_untrusted_current_user" = "If you didn’t sign in to this session, your account may be compromised.";
|
||||
|
||||
"user_verification_session_details_verify_action_current_user" = "Verify";
|
||||
"user_verification_session_details_verify_action_other_user" = "Manually verify";
|
||||
|
|
|
@ -42,6 +42,20 @@ extern NSString *const kMXKRoomBubbleCellLongPressOnReactionView;
|
|||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellEventIdKey;
|
||||
|
||||
/**
|
||||
Action identifier used when the user pressed accept button for an incoming key verification request.
|
||||
|
||||
The `userInfo` dictionary contains an `NSString` object under the `kMXKRoomBubbleCellEventIdKey` key, representing the event id associated to the key verification request.
|
||||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed;
|
||||
|
||||
/**
|
||||
Action identifier used when the user pressed decline button for an incoming key verification request.
|
||||
|
||||
The `userInfo` dictionary contains an `NSString` object under the `kMXKRoomBubbleCellEventIdKey` key, representing the event id associated to the key verification request.
|
||||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed;
|
||||
|
||||
/**
|
||||
Define a `MXKRoomBubbleTableViewCell` category at Riot level to handle bubble customisation.
|
||||
*/
|
||||
|
|
|
@ -31,6 +31,8 @@ NSString *const kMXKRoomBubbleCellRiotEditButtonPressed = @"kMXKRoomBubbleCellRi
|
|||
NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellTapOnReceiptsContainer";
|
||||
NSString *const kMXKRoomBubbleCellLongPressOnReactionView = @"kMXKRoomBubbleCellLongPressOnReactionView";
|
||||
NSString *const kMXKRoomBubbleCellEventIdKey = @"kMXKRoomBubbleCellEventIdKey";
|
||||
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed = @"kMXKRoomBubbleCellKeyVerificationAcceptPressed";
|
||||
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = @"kMXKRoomBubbleCellKeyVerificationDeclinePressed";
|
||||
|
||||
@implementation MXKRoomBubbleTableViewCell (Riot)
|
||||
|
||||
|
@ -76,6 +78,12 @@ NSString *const kMXKRoomBubbleCellEventIdKey = @"kMXKRoomBubbleCellEventIdKey";
|
|||
viewTag:(NSInteger)viewTag
|
||||
displayOnLeft:(BOOL)displayOnLeft
|
||||
{
|
||||
if (!self.bubbleInfoContainer)
|
||||
{
|
||||
NSLog(@"[MXKRoomBubbleTableViewCell+Riot] bubbleInfoContainer property is missing for cell class: %@", NSStringFromClass(self.class));
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *bubbleComponents = bubbleData.bubbleComponents;
|
||||
MXKRoomBubbleComponent *component = bubbleComponents[componentIndex];
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "UserEncryptionTrustLevel.h"
|
||||
|
||||
/**
|
||||
Define a `MXRoom` category at Riot level.
|
||||
*/
|
||||
|
@ -75,4 +77,11 @@
|
|||
*/
|
||||
- (void)allMessages:(void (^)(void))completion;
|
||||
|
||||
/**
|
||||
Get user encryption trust level.
|
||||
|
||||
@param userId The user id.
|
||||
*/
|
||||
- (UserEncryptionTrustLevel)encryptionTrustLevelForUserId:(NSString*)userId;
|
||||
|
||||
@end
|
||||
|
|
|
@ -324,6 +324,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (UserEncryptionTrustLevel)encryptionTrustLevelForUserId:(NSString*)userId
|
||||
{
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
||||
|
||||
if (self.summary.isEncrypted && self.mxSession.crypto)
|
||||
{
|
||||
MXUsersTrustLevelSummary *usersTrustLevelSummary = [self.mxSession.crypto trustLevelSummaryForUserIds:@[userId]];
|
||||
|
||||
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
||||
|
||||
if (trustedDevicesPercentage >= 1.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelTrusted;
|
||||
}
|
||||
else if (trustedDevicesPercentage == 0.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelWarning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelNone;
|
||||
}
|
||||
|
||||
return userEncryptionTrustLevel;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (MXPushRule*)getRoomPushRule
|
||||
|
|
|
@ -16,6 +16,17 @@
|
|||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
/**
|
||||
RoomEncryptionTrustLevel represents the trust level in an encrypted room.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RoomEncryptionTrustLevel) {
|
||||
RoomEncryptionTrustLevelTrusted,
|
||||
RoomEncryptionTrustLevelWarning,
|
||||
RoomEncryptionTrustLevelNormal,
|
||||
RoomEncryptionTrustLevelUnknown
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Define a `MXRoomSummary` category at Riot level.
|
||||
*/
|
||||
|
@ -32,4 +43,11 @@
|
|||
*/
|
||||
- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView;
|
||||
|
||||
/**
|
||||
Get the trust level in the room.
|
||||
|
||||
@return the trust level.
|
||||
*/
|
||||
- (RoomEncryptionTrustLevel)roomEncryptionTrustLevel;
|
||||
|
||||
@end
|
||||
|
|
|
@ -47,4 +47,34 @@
|
|||
mxkImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
}
|
||||
|
||||
- (RoomEncryptionTrustLevel)roomEncryptionTrustLevel
|
||||
{
|
||||
RoomEncryptionTrustLevel roomEncryptionTrustLevel = RoomEncryptionTrustLevelUnknown;
|
||||
if (self.trust)
|
||||
{
|
||||
double trustedUsersPercentage = self.trust.trustedUsersProgress.fractionCompleted;
|
||||
double trustedDevicesPercentage = self.trust.trustedDevicesProgress.fractionCompleted;
|
||||
|
||||
if (trustedUsersPercentage >= 1.0)
|
||||
{
|
||||
if (trustedDevicesPercentage >= 1.0)
|
||||
{
|
||||
roomEncryptionTrustLevel = RoomEncryptionTrustLevelTrusted;
|
||||
}
|
||||
else
|
||||
{
|
||||
roomEncryptionTrustLevel = RoomEncryptionTrustLevelWarning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
roomEncryptionTrustLevel = RoomEncryptionTrustLevelNormal;
|
||||
}
|
||||
|
||||
roomEncryptionTrustLevel = roomEncryptionTrustLevel;
|
||||
}
|
||||
|
||||
return roomEncryptionTrustLevel;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#import "MXSession+Riot.h"
|
||||
|
||||
#import "MXRoom+Riot.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@implementation MXSession (Riot)
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import UIKit
|
|||
|
||||
extension UIStackView {
|
||||
|
||||
func vc_removeAllSubviews() {
|
||||
func vc_removeAllArrangedSubviews() {
|
||||
let subviews = self.arrangedSubviews
|
||||
for subview in subviews {
|
||||
self.removeArrangedSubview(subview)
|
||||
|
|
|
@ -19,7 +19,7 @@ import Foundation
|
|||
extension UIView {
|
||||
|
||||
/// Add a subview matching parent view using autolayout
|
||||
func vc_addSubViewMatchingParent(_ subView: UIView) {
|
||||
@objc func vc_addSubViewMatchingParent(_ subView: UIView) {
|
||||
self.addSubview(subView)
|
||||
subView.translatesAutoresizingMaskIntoConstraints = false
|
||||
let views = ["view": subView]
|
||||
|
@ -31,4 +31,10 @@ extension UIView {
|
|||
constraints.forEach { $0.isActive = true }
|
||||
}
|
||||
}
|
||||
|
||||
@objc func vc_removeAllSubviews() {
|
||||
for subView in self.subviews {
|
||||
subView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,9 @@ internal enum Asset {
|
|||
internal static let e2eBlocked = ImageAsset(name: "e2e_blocked")
|
||||
internal static let e2eUnencrypted = ImageAsset(name: "e2e_unencrypted")
|
||||
internal static let e2eWarning = ImageAsset(name: "e2e_warning")
|
||||
internal static let encryptionNormal = ImageAsset(name: "encryption_normal")
|
||||
internal static let encryptionTrusted = ImageAsset(name: "encryption_trusted")
|
||||
internal static let encryptionWarning = ImageAsset(name: "encryption_warning")
|
||||
internal static let directChatOff = ImageAsset(name: "directChatOff")
|
||||
internal static let directChatOn = ImageAsset(name: "directChatOn")
|
||||
internal static let favourite = ImageAsset(name: "favourite")
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -1470,6 +1470,78 @@ internal enum VectorL10n {
|
|||
internal static var keyBackupSetupTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_setup_title")
|
||||
}
|
||||
/// %@ wants to verify
|
||||
internal static func keyVerificationIncomingRequestIncomingAlertMessage(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "key_verification_incoming_request_incoming_alert_message", p1)
|
||||
}
|
||||
/// Verified
|
||||
internal static var keyVerificationTileConclusionDoneTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_conclusion_done_title")
|
||||
}
|
||||
/// Unstrusted sign in
|
||||
internal static var keyVerificationTileConclusionWarningTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_conclusion_warning_title")
|
||||
}
|
||||
/// Accept
|
||||
internal static var keyVerificationTileRequestIncomingApprovalAccept: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_incoming_approval_accept")
|
||||
}
|
||||
/// Decline
|
||||
internal static var keyVerificationTileRequestIncomingApprovalDecline: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_incoming_approval_decline")
|
||||
}
|
||||
/// Verification request
|
||||
internal static var keyVerificationTileRequestIncomingTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_incoming_title")
|
||||
}
|
||||
/// Verification sent
|
||||
internal static var keyVerificationTileRequestOutgoingTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_outgoing_title")
|
||||
}
|
||||
/// You accepted
|
||||
internal static var keyVerificationTileRequestStatusAccepted: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_accepted")
|
||||
}
|
||||
/// %@ cancelled
|
||||
internal static func keyVerificationTileRequestStatusCancelled(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_cancelled", p1)
|
||||
}
|
||||
/// You cancelled
|
||||
internal static var keyVerificationTileRequestStatusCancelledByMe: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_cancelled_by_me")
|
||||
}
|
||||
/// Data loading…
|
||||
internal static var keyVerificationTileRequestStatusDataLoading: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_data_loading")
|
||||
}
|
||||
/// Expired
|
||||
internal static var keyVerificationTileRequestStatusExpired: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_expired")
|
||||
}
|
||||
/// Waiting…
|
||||
internal static var keyVerificationTileRequestStatusWaiting: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_tile_request_status_waiting")
|
||||
}
|
||||
/// Verify user
|
||||
internal static var keyVerificationUserTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_user_title")
|
||||
}
|
||||
/// You’ve successfully verified this user.
|
||||
internal static var keyVerificationVerifiedUserDescription1: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_verified_user_description_1")
|
||||
}
|
||||
/// Messages with this user in this room are end-to-end encrypted and can’t be read by third parties.
|
||||
internal static var keyVerificationVerifiedUserDescription2: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_verified_user_description_2")
|
||||
}
|
||||
/// Verify this user by confirming the following unique emoji appears on their screen, in the same order.
|
||||
internal static var keyVerificationVerifyUserTitleEmoji: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_verify_user_title_emoji")
|
||||
}
|
||||
/// Verify this user by confirming the following numbers appear on their screen, in the same order.
|
||||
internal static var keyVerificationVerifyUserTitleNumber: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_verify_user_title_number")
|
||||
}
|
||||
/// %.1fK
|
||||
internal static func largeBadgeValueKFormat(_ p1: Float) -> String {
|
||||
return VectorL10n.tr("Vector", "large_badge_value_k_format", p1)
|
||||
|
@ -1482,6 +1554,30 @@ internal enum VectorL10n {
|
|||
internal static var leave: String {
|
||||
return VectorL10n.tr("Vector", "leave")
|
||||
}
|
||||
/// SESSION INFO
|
||||
internal static var manageSessionInfo: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_info")
|
||||
}
|
||||
/// Device name
|
||||
internal static var manageSessionName: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_name")
|
||||
}
|
||||
/// Not trusted
|
||||
internal static var manageSessionNotTrusted: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_not_trusted")
|
||||
}
|
||||
/// Sign out of this device
|
||||
internal static var manageSessionSignOut: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_sign_out")
|
||||
}
|
||||
/// Manage session
|
||||
internal static var manageSessionTitle: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_title")
|
||||
}
|
||||
/// Trusted by you
|
||||
internal static var manageSessionTrusted: String {
|
||||
return VectorL10n.tr("Vector", "manage_session_trusted")
|
||||
}
|
||||
/// Library
|
||||
internal static var mediaPickerLibrary: String {
|
||||
return VectorL10n.tr("Vector", "media_picker_library")
|
||||
|
@ -2178,10 +2274,26 @@ internal enum VectorL10n {
|
|||
internal static var roomParticipantsActionSectionDirectChats: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_section_direct_chats")
|
||||
}
|
||||
/// Other
|
||||
/// Options
|
||||
internal static var roomParticipantsActionSectionOther: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_section_other")
|
||||
}
|
||||
/// Security
|
||||
internal static var roomParticipantsActionSectionSecurity: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_section_security")
|
||||
}
|
||||
/// Verified
|
||||
internal static var roomParticipantsActionSecurityStatusVerified: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_security_status_verified")
|
||||
}
|
||||
/// Verify
|
||||
internal static var roomParticipantsActionSecurityStatusVerify: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_security_status_verify")
|
||||
}
|
||||
/// Warning
|
||||
internal static var roomParticipantsActionSecurityStatusWarning: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_security_status_warning")
|
||||
}
|
||||
/// Make admin
|
||||
internal static var roomParticipantsActionSetAdmin: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_action_set_admin")
|
||||
|
@ -2294,6 +2406,18 @@ internal enum VectorL10n {
|
|||
internal static var roomParticipantsRemoveThirdPartyInvitePromptMsg: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_remove_third_party_invite_prompt_msg")
|
||||
}
|
||||
/// 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.
|
||||
internal static var roomParticipantsSecurityInformationRoomEncrypted: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_security_information_room_encrypted")
|
||||
}
|
||||
/// Messages in this room are not end-to-end encrypted.
|
||||
internal static var roomParticipantsSecurityInformationRoomNotEncrypted: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_security_information_room_not_encrypted")
|
||||
}
|
||||
/// Loading…
|
||||
internal static var roomParticipantsSecurityLoading: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_security_loading")
|
||||
}
|
||||
/// No identity server is configured so you cannot start a chat with a contact using an email.
|
||||
internal static var roomParticipantsStartNewChatErrorUsingUserEmailWithoutIdentityServer: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_start_new_chat_error_using_user_email_without_identity_server")
|
||||
|
@ -2554,6 +2678,38 @@ internal enum VectorL10n {
|
|||
internal static var searchRooms: String {
|
||||
return VectorL10n.tr("Vector", "search_rooms")
|
||||
}
|
||||
/// ADVANCED
|
||||
internal static var securitySettingsAdvanced: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_advanced")
|
||||
}
|
||||
/// MESSAGE BACKUP
|
||||
internal static var securitySettingsBackup: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_backup")
|
||||
}
|
||||
/// Never send messages to untrusted sessions
|
||||
internal static var securitySettingsBlacklistUnverifiedDevices: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_blacklist_unverified_devices")
|
||||
}
|
||||
/// Verify all of a users sessions to mark them as trusted and send messages to them.
|
||||
internal static var securitySettingsBlacklistUnverifiedDevicesDescription: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_blacklist_unverified_devices_description")
|
||||
}
|
||||
/// MY SESSIONS
|
||||
internal static var securitySettingsCryptoSessions: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_crypto_sessions")
|
||||
}
|
||||
/// Trust sessions to grant access to end-to-end encrypted messages. If you don’t recognise a session, change your login password and reset your Message Password used for Message Backup.
|
||||
internal static var securitySettingsCryptoSessionsDescription: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_crypto_sessions_description")
|
||||
}
|
||||
/// Export keys manually
|
||||
internal static var securitySettingsExportKeysManually: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_export_keys_manually")
|
||||
}
|
||||
/// Security
|
||||
internal static var securitySettingsTitle: String {
|
||||
return VectorL10n.tr("Vector", "security_settings_title")
|
||||
}
|
||||
/// Send to %@
|
||||
internal static func sendTo(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "send_to", p1)
|
||||
|
@ -2970,14 +3126,6 @@ internal enum VectorL10n {
|
|||
internal static var settingsLabsCreateConferenceWithJitsi: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_create_conference_with_jitsi")
|
||||
}
|
||||
/// Cross-Signing
|
||||
internal static var settingsLabsCrossSigning: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_cross_signing")
|
||||
}
|
||||
/// Key verification by direct message
|
||||
internal static var settingsLabsDmKeyVerification: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_dm_key_verification")
|
||||
}
|
||||
/// End-to-End Encryption
|
||||
internal static var settingsLabsE2eEncryption: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_e2e_encryption")
|
||||
|
@ -2986,6 +3134,10 @@ internal enum VectorL10n {
|
|||
internal static var settingsLabsE2eEncryptionPromptMessage: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_e2e_encryption_prompt_message")
|
||||
}
|
||||
/// Enable cross-signing to verify per-user instead of per-device (in development)
|
||||
internal static var settingsLabsEnableCrossSigning: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_enable_cross_signing")
|
||||
}
|
||||
/// React to messages with emoji
|
||||
internal static var settingsLabsMessageReaction: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_message_reaction")
|
||||
|
@ -3074,6 +3226,10 @@ internal enum VectorL10n {
|
|||
internal static var settingsReportBug: String {
|
||||
return VectorL10n.tr("Vector", "settings_report_bug")
|
||||
}
|
||||
/// SECURITY
|
||||
internal static var settingsSecurity: String {
|
||||
return VectorL10n.tr("Vector", "settings_security")
|
||||
}
|
||||
/// Send anon crash & usage data
|
||||
internal static var settingsSendCrashReport: String {
|
||||
return VectorL10n.tr("Vector", "settings_send_crash_report")
|
||||
|
@ -3294,6 +3450,98 @@ internal enum VectorL10n {
|
|||
internal static var unknownDevicesVerify: String {
|
||||
return VectorL10n.tr("Vector", "unknown_devices_verify")
|
||||
}
|
||||
/// If you didn’t sign in to this session, your account may be compromised.
|
||||
internal static var userVerificationSessionDetailsAdditionalInformationUntrustedCurrentUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_additional_information_untrusted_current_user")
|
||||
}
|
||||
/// Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it.
|
||||
internal static var userVerificationSessionDetailsAdditionalInformationUntrustedOtherUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_additional_information_untrusted_other_user")
|
||||
}
|
||||
/// This session is trusted for secure messaging because you verified it:
|
||||
internal static var userVerificationSessionDetailsInformationTrustedCurrentUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_information_trusted_current_user")
|
||||
}
|
||||
/// This device is trusted for secure messaging because
|
||||
internal static var userVerificationSessionDetailsInformationTrustedOtherUserPart1: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_information_trusted_other_user_part1")
|
||||
}
|
||||
/// verified it:
|
||||
internal static var userVerificationSessionDetailsInformationTrustedOtherUserPart2: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_information_trusted_other_user_part2")
|
||||
}
|
||||
/// Verify this session to mark it as trusted & grant it access to encrypted messages:
|
||||
internal static var userVerificationSessionDetailsInformationUntrustedCurrentUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_information_untrusted_current_user")
|
||||
}
|
||||
/// signed in using a new device:
|
||||
internal static var userVerificationSessionDetailsInformationUntrustedOtherUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_information_untrusted_other_user")
|
||||
}
|
||||
/// Trusted
|
||||
internal static var userVerificationSessionDetailsTrustedTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_trusted_title")
|
||||
}
|
||||
/// Warning
|
||||
internal static var userVerificationSessionDetailsUntrustedTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_untrusted_title")
|
||||
}
|
||||
/// Verify
|
||||
internal static var userVerificationSessionDetailsVerifyActionCurrentUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_verify_action_current_user")
|
||||
}
|
||||
/// Manually verify
|
||||
internal static var userVerificationSessionDetailsVerifyActionOtherUser: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_session_details_verify_action_other_user")
|
||||
}
|
||||
/// Messages with this user in this room are end-to-end encrypted and can’t be read by third parties.
|
||||
internal static var userVerificationSessionsListInformation: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_information")
|
||||
}
|
||||
/// Trusted
|
||||
internal static var userVerificationSessionsListSessionTrusted: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_session_trusted")
|
||||
}
|
||||
/// Not trusted
|
||||
internal static var userVerificationSessionsListSessionUntrusted: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_session_untrusted")
|
||||
}
|
||||
/// Sessions
|
||||
internal static var userVerificationSessionsListTableTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_table_title")
|
||||
}
|
||||
/// Trusted
|
||||
internal static var userVerificationSessionsListUserTrustLevelTrustedTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_user_trust_level_trusted_title")
|
||||
}
|
||||
/// Unknown
|
||||
internal static var userVerificationSessionsListUserTrustLevelUnknownTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_user_trust_level_unknown_title")
|
||||
}
|
||||
/// Warning
|
||||
internal static var userVerificationSessionsListUserTrustLevelWarningTitle: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_sessions_list_user_trust_level_warning_title")
|
||||
}
|
||||
/// To be secure, do this in person or use another way to communicate.
|
||||
internal static var userVerificationStartAdditionalInformation: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_start_additional_information")
|
||||
}
|
||||
/// For extra security, verify
|
||||
internal static var userVerificationStartInformationPart1: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_start_information_part1")
|
||||
}
|
||||
/// by checking a one-time code on both your devices.
|
||||
internal static var userVerificationStartInformationPart2: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_start_information_part2")
|
||||
}
|
||||
/// Start verification
|
||||
internal static var userVerificationStartVerifyAction: String {
|
||||
return VectorL10n.tr("Vector", "user_verification_start_verify_action")
|
||||
}
|
||||
/// Waiting for %@…
|
||||
internal static func userVerificationStartWaitingPartner(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "user_verification_start_waiting_partner", p1)
|
||||
}
|
||||
/// Video
|
||||
internal static var video: String {
|
||||
return VectorL10n.tr("Vector", "video")
|
||||
|
|
|
@ -56,7 +56,8 @@ final public class OnBoardingManager: NSObject {
|
|||
case .success:
|
||||
|
||||
// Create DM room with Riot-bot
|
||||
let httpOperation = self.session.createRoom(name: nil, visibility: .private, alias: nil, topic: nil, invite: [Constants.riotBotMatrixId], invite3PID: nil, isDirect: true, preset: .trustedPrivateChat) { (response) in
|
||||
let roomCreationParameters = MXRoomCreationParameters(forDirectRoomWithUser: Constants.riotBotMatrixId)
|
||||
let httpOperation = self.session.createRoom(parameters: roomCreationParameters) { (response) in
|
||||
|
||||
switch response {
|
||||
case .success:
|
||||
|
|
|
@ -124,14 +124,6 @@ final class RiotSettings: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
var enableDMKeyVerification: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableDMKeyVerification)
|
||||
} set {
|
||||
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableDMKeyVerification)
|
||||
}
|
||||
}
|
||||
|
||||
var enableCrossSigning: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableCrossSigning)
|
||||
|
|
|
@ -1110,20 +1110,10 @@
|
|||
{
|
||||
MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId];
|
||||
|
||||
[account.mxSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
invite:@[@"@riot-bot:matrix.org"]
|
||||
invite3PID:nil
|
||||
isDirect:YES
|
||||
preset:kMXRoomPresetTrustedPrivateChat
|
||||
success:nil
|
||||
failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[AuthenticationVC] Create chat with riot-bot failed");
|
||||
|
||||
}];
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:@"@riot-bot:matrix.org"];
|
||||
[account.mxSession createRoomWithParameters:roomCreationParameters success:nil failure:^(NSError *error) {
|
||||
NSLog(@"[AuthenticationVC] Create chat with riot-bot failed");
|
||||
}];
|
||||
}
|
||||
|
||||
// Remove auth view controller on successful login
|
||||
|
|
|
@ -137,7 +137,15 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
|||
|
||||
self.directRoomBorderView.hidden = !roomCellData.roomSummary.room.isDirect;
|
||||
|
||||
self.encryptedRoomIcon.hidden = !roomCellData.roomSummary.isEncrypted;
|
||||
if (roomCellData.roomSummary.isEncrypted)
|
||||
{
|
||||
self.encryptedRoomIcon.hidden = NO;
|
||||
self.encryptedRoomIcon.image = [self shieldImageForTrustLevel:roomCellData.roomSummary.roomEncryptionTrustLevel];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.encryptedRoomIcon.hidden = YES;
|
||||
}
|
||||
|
||||
[roomCellData.roomSummary setRoomAvatarImageIn:self.roomAvatar];
|
||||
}
|
||||
|
@ -153,4 +161,32 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
|||
return 74;
|
||||
}
|
||||
|
||||
- (UIImage*)shieldImageForTrustLevel:(RoomEncryptionTrustLevel)roomEncryptionTrustLevel
|
||||
{
|
||||
UIImage *shieldImage;
|
||||
|
||||
NSString *encryptionIconName;
|
||||
switch (roomEncryptionTrustLevel)
|
||||
{
|
||||
case RoomEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelUnknown:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
shieldImage = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
return shieldImage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,11 +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"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -75,10 +75,10 @@
|
|||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OeZ-wN-eil">
|
||||
<rect key="frame" x="524" y="14" width="0.0" height="20"/>
|
||||
<rect key="frame" x="524" y="13.5" width="0.0" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mbn-A1-1Yw">
|
||||
<rect key="frame" x="-4" y="2" width="9" height="17"/>
|
||||
<rect key="frame" x="-4.5" y="1.5" width="9" height="17"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MissedNotifAndUnreadBadge"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -94,11 +94,11 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="e2e_verified" translatesAutoresizingMaskIntoConstraints="NO" id="NAZ-zd-MHS">
|
||||
<rect key="frame" x="50" y="42" width="11" height="13"/>
|
||||
<rect key="frame" x="44" y="43" width="16" height="16"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="EncryptedRoomIcon"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="13" id="QdE-73-LyZ"/>
|
||||
<constraint firstAttribute="width" constant="11" id="i9u-Bs-pBy"/>
|
||||
<constraint firstAttribute="height" constant="16" id="QdE-73-LyZ"/>
|
||||
<constraint firstAttribute="width" constant="16" id="i9u-Bs-pBy"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
|
@ -112,11 +112,11 @@
|
|||
<constraint firstItem="dQt-mN-T6b" firstAttribute="leading" secondItem="RX5-eD-c3c" secondAttribute="trailing" constant="14" id="XFM-LG-4uJ"/>
|
||||
<constraint firstItem="360-Go-RcG" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="16" id="XyO-tl-6SX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="360-Go-RcG" secondAttribute="trailing" constant="10" id="YqC-WC-Wqe"/>
|
||||
<constraint firstItem="NAZ-zd-MHS" firstAttribute="centerY" secondItem="dQt-mN-T6b" secondAttribute="centerY" id="c6L-cn-sJL"/>
|
||||
<constraint firstItem="NAZ-zd-MHS" firstAttribute="centerY" secondItem="dQt-mN-T6b" secondAttribute="centerY" constant="2" id="c6L-cn-sJL"/>
|
||||
<constraint firstItem="OeZ-wN-eil" firstAttribute="centerY" secondItem="360-Go-RcG" secondAttribute="centerY" id="fYc-th-Nay"/>
|
||||
<constraint firstItem="dQt-mN-T6b" firstAttribute="top" secondItem="Lg1-xQ-AGn" secondAttribute="bottom" constant="4" id="iaT-57-GOs"/>
|
||||
<constraint firstItem="RX5-eD-c3c" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="15" id="mga-fG-I0L"/>
|
||||
<constraint firstItem="NAZ-zd-MHS" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="50" id="ofi-HP-uke"/>
|
||||
<constraint firstItem="NAZ-zd-MHS" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="44" id="ofi-HP-uke"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dQt-mN-T6b" secondAttribute="trailing" constant="10" id="t2m-pb-5zd"/>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="14" id="tY3-6V-A3B"/>
|
||||
<constraint firstItem="RX5-eD-c3c" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="13" id="tgy-cX-Wxm"/>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#import "AppDelegate.h"
|
||||
#import "Riot-Swift.h"
|
||||
#import "MXSession+Riot.h"
|
||||
|
||||
#import "RoomMemberTitleView.h"
|
||||
|
||||
|
@ -1041,36 +1042,51 @@
|
|||
inviteArray = @[participantId];
|
||||
}
|
||||
|
||||
MXWeakify(self);
|
||||
void (^onFailure)(NSError *) = ^(NSError *error){
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed");
|
||||
|
||||
self->roomCreationRequest = nil;
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
};
|
||||
|
||||
|
||||
// Create a new room
|
||||
roomCreationRequest = [self.mainSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
invite:inviteArray
|
||||
invite3PID:invite3PIDArray
|
||||
isDirect:YES
|
||||
preset:kMXRoomPresetTrustedPrivateChat
|
||||
success:^(MXRoom *room) {
|
||||
[self.mainSession canEnableE2EByDefaultInNewRoomWithUsers:inviteArray success:^(BOOL canEnableE2E) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
roomCreationRequest = nil;
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new];
|
||||
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate;
|
||||
roomCreationParameters.inviteArray = inviteArray;
|
||||
roomCreationParameters.invite3PIDArray = invite3PIDArray;
|
||||
roomCreationParameters.isDirect = YES;
|
||||
roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat;
|
||||
|
||||
[self removePendingActionMask];
|
||||
if (canEnableE2E && roomCreationParameters.invite3PIDArray == nil)
|
||||
{
|
||||
roomCreationParameters.initialStateEvents = @[
|
||||
[MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm
|
||||
]];
|
||||
}
|
||||
|
||||
[[AppDelegate theDelegate] showRoom:room.roomId andEventId:nil withMatrixSession:self.mainSession];
|
||||
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
self->roomCreationRequest = [self.mainSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed");
|
||||
self->roomCreationRequest = nil;
|
||||
|
||||
roomCreationRequest = nil;
|
||||
[self removePendingActionMask];
|
||||
|
||||
[self removePendingActionMask];
|
||||
[[AppDelegate theDelegate] showRoom:room.roomId andEventId:nil withMatrixSession:self.mainSession];
|
||||
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
} failure:onFailure];
|
||||
|
||||
}];
|
||||
} failure:onFailure];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1093,36 +1109,28 @@
|
|||
else
|
||||
{
|
||||
// Create a new room
|
||||
roomCreationRequest = [self.mainSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
invite:@[matrixId]
|
||||
invite3PID:nil
|
||||
isDirect:YES
|
||||
preset:kMXRoomPresetTrustedPrivateChat
|
||||
success:^(MXRoom *room) {
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:matrixId];
|
||||
roomCreationRequest = [self.mainSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
|
||||
|
||||
roomCreationRequest = nil;
|
||||
roomCreationRequest = nil;
|
||||
|
||||
// Delay the call in order to be sure that the room is ready
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[room placeCallWithVideo:isVideoCall success:nil failure:nil];
|
||||
[self removePendingActionMask];
|
||||
});
|
||||
// Delay the call in order to be sure that the room is ready
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[room placeCallWithVideo:isVideoCall success:nil failure:nil];
|
||||
[self removePendingActionMask];
|
||||
});
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed");
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed");
|
||||
|
||||
roomCreationRequest = nil;
|
||||
roomCreationRequest = nil;
|
||||
|
||||
[self removePendingActionMask];
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
@property (nonatomic) IBOutlet UILabel *contactDisplayNameLabel;
|
||||
@property (nonatomic) IBOutlet UILabel *contactInformationLabel;
|
||||
@property (nonatomic) IBOutlet UIView *customAccessoryView;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *avatarBadgeImageView;
|
||||
|
||||
@property (nonatomic) BOOL showCustomAccessoryView;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#import "AvatarGenerator.h"
|
||||
#import "Tools.h"
|
||||
#import "MXRoom+Riot.h"
|
||||
|
||||
#import "NBPhoneNumberUtil.h"
|
||||
|
||||
|
@ -171,6 +172,7 @@
|
|||
}];
|
||||
|
||||
[self refreshContactPresence];
|
||||
[self refreshContactBadgeImage];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -234,6 +236,47 @@
|
|||
self.thumbnailView.image = image;
|
||||
}
|
||||
|
||||
- (void)refreshContactBadgeImage
|
||||
{
|
||||
NSString *matrixId = [self firstMatrixId];
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel = UserEncryptionTrustLevelUnknown;
|
||||
|
||||
if (matrixId)
|
||||
{
|
||||
userEncryptionTrustLevel = [self.mxRoom encryptionTrustLevelForUserId:matrixId];
|
||||
}
|
||||
|
||||
self.avatarBadgeImageView.image = [self badgeImageForUserEncryptionTrustLevel:userEncryptionTrustLevel];
|
||||
}
|
||||
|
||||
- (UIImage*)badgeImageForUserEncryptionTrustLevel:(UserEncryptionTrustLevel)userEncryptionTrustLevel
|
||||
{
|
||||
NSString *encryptionIconName;
|
||||
UIImage *encryptionIcon;
|
||||
|
||||
switch (userEncryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
encryptionIcon = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
|
||||
return encryptionIcon;
|
||||
}
|
||||
|
||||
- (void)refreshContactDisplayName
|
||||
{
|
||||
self.contactDisplayNameLabel.text = contact.displayName;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" 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="13772"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -27,6 +27,13 @@
|
|||
<constraint firstAttribute="height" constant="42" id="WPC-tL-hnM"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Wg7-no-cax">
|
||||
<rect key="frame" x="41" y="43" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="KYi-Aj-oCp"/>
|
||||
<constraint firstAttribute="width" secondItem="Wg7-no-cax" secondAttribute="height" multiplier="1:1" id="Lh5-LD-3eg"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="e2Q-sr-Kz4">
|
||||
<rect key="frame" x="42" y="10" width="20" height="21"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="ThumbnailBadgeView"/>
|
||||
|
@ -43,7 +50,7 @@
|
|||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="info label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQt-mN-T6b">
|
||||
<rect key="frame" x="69" y="39" width="531" height="20"/>
|
||||
<rect key="frame" x="69" y="39" width="531" height="19.5"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="ContactInformationLabel"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -60,6 +67,7 @@
|
|||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Wg7-no-cax" firstAttribute="width" secondItem="Wg7-no-cax" secondAttribute="height" multiplier="1:1" id="07s-nq-mon"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="RX5-eD-c3c" secondAttribute="bottom" constant="15" id="0MH-4P-AYF"/>
|
||||
<constraint firstAttribute="bottom" secondItem="dQt-mN-T6b" secondAttribute="bottom" constant="15" id="2cU-tU-nDT"/>
|
||||
<constraint firstItem="e2Q-sr-Kz4" firstAttribute="trailing" secondItem="RX5-eD-c3c" secondAttribute="trailing" constant="7" id="Fib-p7-t8G"/>
|
||||
|
@ -68,10 +76,13 @@
|
|||
<constraint firstItem="dQt-mN-T6b" firstAttribute="top" secondItem="Lg1-xQ-AGn" secondAttribute="bottom" constant="4" id="iaT-57-GOs"/>
|
||||
<constraint firstItem="RX5-eD-c3c" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="15" id="mga-fG-I0L"/>
|
||||
<constraint firstItem="Ogo-Qt-u2C" firstAttribute="leading" secondItem="Lg1-xQ-AGn" secondAttribute="trailing" constant="13" id="oNi-JM-zCJ"/>
|
||||
<constraint firstItem="Wg7-no-cax" firstAttribute="bottom" secondItem="RX5-eD-c3c" secondAttribute="bottom" constant="2" id="pck-mZ-FjJ"/>
|
||||
<constraint firstItem="Wg7-no-cax" firstAttribute="width" secondItem="Wg7-no-cax" secondAttribute="height" multiplier="1:1" id="sZo-F2-fpL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dQt-mN-T6b" secondAttribute="trailing" id="t2m-pb-5zd"/>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="14" id="tY3-6V-A3B"/>
|
||||
<constraint firstItem="RX5-eD-c3c" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="13" id="tgy-cX-Wxm"/>
|
||||
<constraint firstItem="RX5-eD-c3c" firstAttribute="top" secondItem="e2Q-sr-Kz4" secondAttribute="top" constant="5" id="xrs-nE-h6t"/>
|
||||
<constraint firstItem="Wg7-no-cax" firstAttribute="trailing" secondItem="RX5-eD-c3c" secondAttribute="trailing" constant="2" id="zT4-CH-fH8"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -81,6 +92,7 @@
|
|||
<constraint firstAttribute="trailing" secondItem="Ogo-Qt-u2C" secondAttribute="trailing" constant="13" id="cWl-2f-cvI"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="avatarBadgeImageView" destination="Wg7-no-cax" id="mTr-4H-B6t"/>
|
||||
<outlet property="contactDisplayNameLabel" destination="Lg1-xQ-AGn" id="xKB-Pw-12c"/>
|
||||
<outlet property="contactInformationLabel" destination="dQt-mN-T6b" id="IKZ-wv-xVD"/>
|
||||
<outlet property="customAccessViewWidthConstraint" destination="pDU-SS-0mb" id="A22-tz-iIz"/>
|
||||
|
|
|
@ -31,6 +31,10 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
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 +58,13 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
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
|
||||
///
|
||||
/// - Parameters:
|
||||
|
@ -66,15 +77,55 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
self.incomingTransaction = incomingTransaction
|
||||
}
|
||||
|
||||
/// Contrustor to manage an incoming SAS device verification transaction
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - session: the MXSession
|
||||
/// - incomingKeyVerificationRequest: An existing incoming key verification request to accept
|
||||
convenience init(session: MXSession, incomingKeyVerificationRequest: MXKeyVerificationRequest) {
|
||||
self.init(session: session, otherUserId: incomingKeyVerificationRequest.sender, otherDeviceId: incomingKeyVerificationRequest.fromDevice)
|
||||
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 = self.createDataLoadingScreenCoordinator()
|
||||
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()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +143,22 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
return coordinator
|
||||
}
|
||||
|
||||
private func createDataLoadingScreenCoordinator(with keyVerificationRequest: MXKeyVerificationRequest) -> DeviceVerificationDataLoadingCoordinator {
|
||||
let coordinator = DeviceVerificationDataLoadingCoordinator(incomingKeyVerificationRequest: keyVerificationRequest)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
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)
|
||||
coordinator.delegate = self
|
||||
|
@ -115,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()
|
||||
|
||||
|
@ -126,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)
|
||||
}
|
||||
|
@ -142,6 +209,16 @@ extension DeviceVerificationCoordinator: DeviceVerificationDataLoadingCoordinato
|
|||
}
|
||||
}
|
||||
|
||||
func deviceVerificationDataLoadingCoordinator(_ coordinator: DeviceVerificationDataLoadingCoordinatorType, didAcceptKeyVerificationRequestWithTransaction transaction: MXDeviceVerificationTransaction) {
|
||||
|
||||
if let sasTransaction = transaction as? MXSASTransaction {
|
||||
self.showVerify(transaction: sasTransaction, animated: true)
|
||||
} else {
|
||||
NSLog("[DeviceVerificationCoordinator] Transaction \(transaction) is not supported")
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
func deviceVerificationDataLoadingCoordinatorDidCancel(_ coordinator: DeviceVerificationDataLoadingCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
|
@ -190,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,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) {
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Present incoming verification from \(viewController)")
|
||||
|
@ -76,6 +88,18 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func present(from viewController: UIViewController, incomingKeyVerificationRequest: MXKeyVerificationRequest, animated: Bool) {
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Present incoming key verification request from \(viewController)")
|
||||
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, incomingKeyVerificationRequest: incomingKeyVerificationRequest)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
|
|
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
|
||||
}
|
|
@ -25,7 +25,6 @@ final class DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoad
|
|||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var deviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadingViewModelType
|
||||
private let deviceVerificationDataLoadingViewController: DeviceVerificationDataLoadingViewController
|
||||
|
||||
|
@ -39,9 +38,14 @@ final class DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoad
|
|||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUserId: String, otherDeviceId: String) {
|
||||
self.session = session
|
||||
let deviceVerificationDataLoadingViewModel = DeviceVerificationDataLoadingViewModel(session: session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
let deviceVerificationDataLoadingViewController = DeviceVerificationDataLoadingViewController.instantiate(with: deviceVerificationDataLoadingViewModel)
|
||||
self.deviceVerificationDataLoadingViewModel = deviceVerificationDataLoadingViewModel
|
||||
self.deviceVerificationDataLoadingViewController = deviceVerificationDataLoadingViewController
|
||||
}
|
||||
|
||||
let deviceVerificationDataLoadingViewModel = DeviceVerificationDataLoadingViewModel(session: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
init(incomingKeyVerificationRequest: MXKeyVerificationRequest) {
|
||||
let deviceVerificationDataLoadingViewModel = DeviceVerificationDataLoadingViewModel(keyVerificationRequest: incomingKeyVerificationRequest)
|
||||
let deviceVerificationDataLoadingViewController = DeviceVerificationDataLoadingViewController.instantiate(with: deviceVerificationDataLoadingViewModel)
|
||||
self.deviceVerificationDataLoadingViewModel = deviceVerificationDataLoadingViewModel
|
||||
self.deviceVerificationDataLoadingViewController = deviceVerificationDataLoadingViewController
|
||||
|
@ -60,6 +64,11 @@ final class DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoad
|
|||
|
||||
// MARK: - DeviceVerificationDataLoadingViewModelCoordinatorDelegate
|
||||
extension DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoadingViewModelCoordinatorDelegate {
|
||||
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didAcceptKeyVerificationWithTransaction transaction: MXDeviceVerificationTransaction) {
|
||||
self.delegate?.deviceVerificationDataLoadingCoordinator(self, didAcceptKeyVerificationRequestWithTransaction: transaction)
|
||||
}
|
||||
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didLoadUser user: MXUser, device: MXDeviceInfo) {
|
||||
self.delegate?.deviceVerificationDataLoadingCoordinator(self, didLoadUser: user, device: device)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import Foundation
|
|||
|
||||
protocol DeviceVerificationDataLoadingCoordinatorDelegate: class {
|
||||
func deviceVerificationDataLoadingCoordinator(_ coordinator: DeviceVerificationDataLoadingCoordinatorType, didLoadUser user: MXUser, device: MXDeviceInfo)
|
||||
func deviceVerificationDataLoadingCoordinator(_ coordinator: DeviceVerificationDataLoadingCoordinatorType, didAcceptKeyVerificationRequestWithTransaction transaction: MXDeviceVerificationTransaction)
|
||||
func deviceVerificationDataLoadingCoordinatorDidCancel(_ coordinator: DeviceVerificationDataLoadingCoordinatorType)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
@ -112,9 +119,35 @@ final class DeviceVerificationDataLoadingViewController: UIViewController {
|
|||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
})
|
||||
|
||||
var shouldDisplayError = true
|
||||
var message: String?
|
||||
|
||||
switch error {
|
||||
case DeviceVerificationDataLoadingViewModelError.transactionCancelled:
|
||||
message = VectorL10n.deviceVerificationCancelled
|
||||
case DeviceVerificationDataLoadingViewModelError.transactionCancelledByMe(reason: let reason):
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
message = VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable)
|
||||
} else {
|
||||
shouldDisplayError = false
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if shouldDisplayError {
|
||||
|
||||
let completion = {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
|
||||
if let message = message {
|
||||
self.errorPresenter.presentError(from: self, title: "", message: message, animated: true, handler: completion)
|
||||
} else {
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func renderError(message: String) {
|
||||
|
|
|
@ -20,6 +20,8 @@ import Foundation
|
|||
|
||||
enum DeviceVerificationDataLoadingViewModelError: Error {
|
||||
case unknown
|
||||
case transactionCancelled
|
||||
case transactionCancelledByMe(reason: MXTransactionCancelCode)
|
||||
}
|
||||
|
||||
final class DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadingViewModelType {
|
||||
|
@ -28,9 +30,13 @@ final class DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadin
|
|||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let otherUserId: String
|
||||
private let otherDeviceId: String
|
||||
private let session: MXSession?
|
||||
private let otherUserId: String?
|
||||
private let otherDeviceId: String?
|
||||
|
||||
private let keyVerificationRequest: MXKeyVerificationRequest?
|
||||
|
||||
private var currentOperation: MXHTTPOperation?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -43,9 +49,18 @@ final class DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadin
|
|||
self.session = session
|
||||
self.otherUserId = otherUserId
|
||||
self.otherDeviceId = otherDeviceId
|
||||
self.keyVerificationRequest = nil
|
||||
}
|
||||
|
||||
init(keyVerificationRequest: MXKeyVerificationRequest) {
|
||||
self.session = nil
|
||||
self.otherUserId = nil
|
||||
self.otherDeviceId = nil
|
||||
self.keyVerificationRequest = keyVerificationRequest
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.currentOperation?.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
@ -62,36 +77,70 @@ final class DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadin
|
|||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
guard let crypto = self.session.crypto else {
|
||||
if let keyVerificationRequest = self.keyVerificationRequest {
|
||||
self.acceptKeyVerificationRequest(keyVerificationRequest)
|
||||
} else {
|
||||
self.downloadOtherDeviceKeys()
|
||||
}
|
||||
}
|
||||
|
||||
private func acceptKeyVerificationRequest(_ keyVerificationRequest: MXKeyVerificationRequest) {
|
||||
|
||||
self.update(viewState: .loading)
|
||||
|
||||
keyVerificationRequest.accept(withMethod: MXKeyVerificationMethodSAS, success: { [weak self] (deviceVerificationTransaction) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let outgoingSASTransaction = deviceVerificationTransaction as? MXOutgoingSASTransaction {
|
||||
self.registerTransactionDidStateChangeNotification(transaction: outgoingSASTransaction)
|
||||
} else {
|
||||
self.update(viewState: .error(DeviceVerificationDataLoadingViewModelError.unknown))
|
||||
}
|
||||
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func downloadOtherDeviceKeys() {
|
||||
guard let session = self.session,
|
||||
let crypto = session.crypto,
|
||||
let otherUserId = self.otherUserId,
|
||||
let otherDeviceId = self.otherDeviceId else {
|
||||
self.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
NSLog("[DeviceVerificationDataLoadingViewModel] Error session.crypto is nil")
|
||||
return
|
||||
}
|
||||
|
||||
if let otherUser = self.session.user(withUserId: otherUserId) {
|
||||
if let otherUser = session.user(withUserId: otherUserId) {
|
||||
|
||||
self.update(viewState: .loading)
|
||||
|
||||
crypto.downloadKeys([self.otherUserId], forceDownload: false, success: { [weak self] (usersDevicesMap) in
|
||||
self.currentOperation = crypto.downloadKeys([otherUserId], forceDownload: false, success: { [weak self] (usersDevicesMap, crossSigningKeysMap) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let otherDevice = usersDevicesMap?.object(forDevice: sself.otherDeviceId, forUser: sself.otherUserId) {
|
||||
if let otherDevice = usersDevicesMap?.object(forDevice: otherDeviceId, forUser: otherUserId) {
|
||||
sself.update(viewState: .loaded)
|
||||
sself.coordinatorDelegate?.deviceVerificationDataLoadingViewModel(sself, didLoadUser: otherUser, device: otherDevice)
|
||||
} else {
|
||||
sself.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
sself.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
}
|
||||
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let finalError = error ?? DeviceVerificationDataLoadingViewModelError.unknown
|
||||
let finalError = error ?? DeviceVerificationDataLoadingViewModelError.unknown
|
||||
|
||||
sself.update(viewState: .error(finalError))
|
||||
sself.update(viewState: .error(finalError))
|
||||
})
|
||||
|
||||
} else {
|
||||
|
@ -102,4 +151,38 @@ final class DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadin
|
|||
private func update(viewState: DeviceVerificationDataLoadingViewState) {
|
||||
self.viewDelegate?.deviceVerificationDataLoadingViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
// MARK: MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification(transaction: MXOutgoingSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
private func unregisterTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXDeviceVerificationTransactionDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXOutgoingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .loaded)
|
||||
self.coordinatorDelegate?.deviceVerificationDataLoadingViewModel(self, didAcceptKeyVerificationWithTransaction: transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .error(DeviceVerificationDataLoadingViewModelError.transactionCancelled))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .error(DeviceVerificationDataLoadingViewModelError.transactionCancelledByMe(reason: reason)))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ protocol DeviceVerificationDataLoadingViewModelViewDelegate: class {
|
|||
|
||||
protocol DeviceVerificationDataLoadingViewModelCoordinatorDelegate: class {
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didLoadUser user: MXUser, device: MXDeviceInfo)
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didAcceptKeyVerificationWithTransaction transaction: MXDeviceVerificationTransaction)
|
||||
func deviceVerificationDataLoadingViewModelDidCancel(_ viewModel: DeviceVerificationDataLoadingViewModelType)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = VectorL10n.keyVerificationUserTitle
|
||||
bodyTitle = VectorL10n.deviceVerificationVerifiedTitle
|
||||
descriptionTextPart1 = VectorL10n.keyVerificationVerifiedUserDescription1
|
||||
descriptionTextPart2 = VectorL10n.keyVerificationVerifiedUserDescription2
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice
|
||||
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 = VectorL10n.keyVerificationUserTitle
|
||||
instructionText = isVerificationByEmoji ? VectorL10n.keyVerificationVerifyUserTitleEmoji : VectorL10n.keyVerificationVerifyUserTitleNumber
|
||||
adviceText = VectorL10n.deviceVerificationSecurityAdvice
|
||||
}
|
||||
|
||||
self.title = title
|
||||
self.titleLabel.text = instructionText
|
||||
self.informationLabel.text = adviceText
|
||||
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner
|
||||
|
||||
self.waitingPartnerLabel.isHidden = true
|
||||
|
@ -224,9 +240,7 @@ 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!
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
- (void)onButtonPressed:(id)sender
|
||||
{
|
||||
UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController;
|
||||
if (sender == self.verifyButton && self.mxDeviceInfo.verified != MXDeviceVerified
|
||||
if (sender == self.verifyButton && self.mxDeviceInfo.trustLevel.localVerificationStatus != MXDeviceVerified
|
||||
&& self.mxDeviceInfo
|
||||
&& rootViewController)
|
||||
{
|
||||
|
|
|
@ -159,7 +159,15 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
|||
|
||||
self.directRoomBorderView.hidden = !roomCellData.roomSummary.room.isDirect;
|
||||
|
||||
self.encryptedRoomIcon.hidden = !roomCellData.roomSummary.isEncrypted;
|
||||
if (roomCellData.roomSummary.isEncrypted)
|
||||
{
|
||||
self.encryptedRoomIcon.hidden = NO;
|
||||
self.encryptedRoomIcon.image = [self shieldImageForTrustLevel:roomCellData.roomSummary.roomEncryptionTrustLevel];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.encryptedRoomIcon.hidden = YES;
|
||||
}
|
||||
|
||||
[roomCellData.roomSummary setRoomAvatarImageIn:self.roomAvatar];
|
||||
}
|
||||
|
@ -207,5 +215,33 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (UIImage*)shieldImageForTrustLevel:(RoomEncryptionTrustLevel)roomEncryptionTrustLevel
|
||||
{
|
||||
UIImage *shieldImage;
|
||||
|
||||
NSString *encryptionIconName;
|
||||
switch (roomEncryptionTrustLevel)
|
||||
{
|
||||
case RoomEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelUnknown:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
shieldImage = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
return shieldImage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -39,7 +38,7 @@
|
|||
<rect key="frame" x="80" y="5" width="0.0" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZUZ-tv-dVV">
|
||||
<rect key="frame" x="-4" y="2" width="9" height="17"/>
|
||||
<rect key="frame" x="-4.5" y="1.5" width="9" height="17"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MissedNotifAndUnreadBadge"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -55,10 +54,10 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="e2e_verified" translatesAutoresizingMaskIntoConstraints="NO" id="5Yd-df-HbB">
|
||||
<rect key="frame" x="53" y="55" width="15" height="18"/>
|
||||
<rect key="frame" x="48" y="50" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="UEZ-5S-KMf"/>
|
||||
<constraint firstAttribute="width" constant="15" id="bbS-D2-SZi"/>
|
||||
<constraint firstAttribute="height" constant="24" id="UEZ-5S-KMf"/>
|
||||
<constraint firstAttribute="width" constant="24" id="bbS-D2-SZi"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="RoomTitle" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oxX-IL-dG4">
|
||||
|
@ -93,7 +92,7 @@
|
|||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="5Yd-df-HbB" secondAttribute="trailing" constant="12" id="0gr-xn-dfD"/>
|
||||
<constraint firstAttribute="trailing" secondItem="5Yd-df-HbB" secondAttribute="trailing" constant="8" id="0gr-xn-dfD"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="oxX-IL-dG4" secondAttribute="bottom" constant="5" id="1JB-d1-zb9"/>
|
||||
<constraint firstItem="oxX-IL-dG4" firstAttribute="top" secondItem="T1Q-RS-8o6" secondAttribute="bottom" constant="4" id="2DB-H2-E2v"/>
|
||||
<constraint firstAttribute="bottom" secondItem="jta-3V-4wL" secondAttribute="bottom" id="3rt-Ig-1rG"/>
|
||||
|
@ -108,7 +107,7 @@
|
|||
<constraint firstItem="T1Q-RS-8o6" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" constant="10" id="cc7-bg-15Z"/>
|
||||
<constraint firstItem="Jkz-Zp-aaG" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" constant="7" id="fPh-Lb-Bv9"/>
|
||||
<constraint firstItem="xws-BR-H47" firstAttribute="centerY" secondItem="T1Q-RS-8o6" secondAttribute="centerY" id="faX-hg-WfP"/>
|
||||
<constraint firstItem="5Yd-df-HbB" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" constant="55" id="h4R-2d-Xxn"/>
|
||||
<constraint firstItem="5Yd-df-HbB" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" constant="50" id="h4R-2d-Xxn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="oxX-IL-dG4" secondAttribute="trailing" constant="4" id="hDl-X9-M4n"/>
|
||||
<constraint firstItem="T1Q-RS-8o6" firstAttribute="centerX" secondItem="eCk-zY-LXq" secondAttribute="centerX" id="hmB-fl-oN2"/>
|
||||
<constraint firstItem="Q6g-b0-3sZ" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" constant="5" id="jST-Ic-lsn"/>
|
||||
|
|
|
@ -21,7 +21,11 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
|
|||
{
|
||||
RoomBubbleCellDataTagMessage = 0, // Default value used for messages
|
||||
RoomBubbleCellDataTagMembership,
|
||||
RoomBubbleCellDataTagRoomCreateWithPredecessor
|
||||
RoomBubbleCellDataTagRoomCreateWithPredecessor,
|
||||
RoomBubbleCellDataTagKeyVerificationNoDisplay,
|
||||
RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval,
|
||||
RoomBubbleCellDataTagKeyVerificationRequest,
|
||||
RoomBubbleCellDataTagKeyVerificationConclusion
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -70,6 +74,16 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
|
|||
*/
|
||||
@property(nonatomic, readonly) CGFloat additionalContentHeight;
|
||||
|
||||
/**
|
||||
MXKeyVerification object associated to key verifcation event when using key verification by direct message.
|
||||
*/
|
||||
@property(nonatomic, strong) MXKeyVerification *keyVerification;
|
||||
|
||||
/**
|
||||
Indicate if there is a pending operation that updates `keyVerification` property.
|
||||
*/
|
||||
@property(nonatomic) BOOL isKeyVerificationOperationPending;
|
||||
|
||||
/**
|
||||
Indicate to update additional content height.
|
||||
*/
|
||||
|
|
|
@ -62,28 +62,36 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
|
|||
|
||||
if (self)
|
||||
{
|
||||
if (event.eventType == MXEventTypeRoomMember)
|
||||
switch (event.eventType)
|
||||
{
|
||||
// Membership events have their own cell type
|
||||
self.tag = RoomBubbleCellDataTagMembership;
|
||||
|
||||
// Membership events can be collapsed together
|
||||
self.collapsable = YES;
|
||||
|
||||
// Collapse them by default
|
||||
self.collapsed = YES;
|
||||
}
|
||||
|
||||
if (event.eventType == MXEventTypeRoomCreate)
|
||||
{
|
||||
MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:event.content];
|
||||
|
||||
if (createContent.roomPredecessorInfo)
|
||||
case MXEventTypeRoomMember:
|
||||
{
|
||||
self.tag = RoomBubbleCellDataTagRoomCreateWithPredecessor;
|
||||
// Membership events have their own cell type
|
||||
self.tag = RoomBubbleCellDataTagMembership;
|
||||
|
||||
// Membership events can be collapsed together
|
||||
self.collapsable = YES;
|
||||
|
||||
// Collapse them by default
|
||||
self.collapsed = YES;
|
||||
}
|
||||
break;
|
||||
case MXEventTypeRoomCreate:
|
||||
{
|
||||
MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:event.content];
|
||||
|
||||
if (createContent.roomPredecessorInfo)
|
||||
{
|
||||
self.tag = RoomBubbleCellDataTagRoomCreateWithPredecessor;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[self keyVerificationDidUpdate];
|
||||
|
||||
// Increase maximum number of components
|
||||
self.maxComponentCount = 20;
|
||||
|
||||
|
@ -147,6 +155,16 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
|
|||
return attributedTextMessage;
|
||||
}
|
||||
|
||||
- (BOOL)hasNoDisplay
|
||||
{
|
||||
if (self.tag == RoomBubbleCellDataTagKeyVerificationNoDisplay)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [super hasNoDisplay];
|
||||
}
|
||||
|
||||
#pragma mark - Bubble collapsing
|
||||
|
||||
- (BOOL)collapseWith:(id<MXKRoomBubbleCellDataStoring>)cellData
|
||||
|
@ -663,21 +681,154 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
|
|||
|
||||
- (BOOL)addEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState
|
||||
{
|
||||
if (self.tag == RoomBubbleCellDataTagMembership || event.eventType == MXEventTypeRoomMember)
|
||||
BOOL shouldAddEvent = YES;
|
||||
|
||||
switch (self.tag)
|
||||
{
|
||||
// One single bubble per membership event
|
||||
return NO;
|
||||
case RoomBubbleCellDataTagKeyVerificationNoDisplay:
|
||||
case RoomBubbleCellDataTagKeyVerificationRequest:
|
||||
case RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval:
|
||||
case RoomBubbleCellDataTagKeyVerificationConclusion:
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
case RoomBubbleCellDataTagRoomCreateWithPredecessor:
|
||||
// We do not want to merge room create event cells with other cell types
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
case RoomBubbleCellDataTagMembership:
|
||||
// One single bubble per membership event
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (self.tag == RoomBubbleCellDataTagRoomCreateWithPredecessor || event.eventType == MXEventTypeRoomCreate)
|
||||
if (shouldAddEvent)
|
||||
{
|
||||
// We do not want to merge room create event cells with other cell types
|
||||
return NO;
|
||||
switch (event.eventType)
|
||||
{
|
||||
case MXEventTypeRoomMessage:
|
||||
{
|
||||
NSString *messageType = event.content[@"msgtype"];
|
||||
|
||||
if ([messageType isEqualToString:kMXMessageTypeKeyVerificationRequest])
|
||||
{
|
||||
shouldAddEvent = NO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MXEventTypeKeyVerificationStart:
|
||||
case MXEventTypeKeyVerificationAccept:
|
||||
case MXEventTypeKeyVerificationKey:
|
||||
case MXEventTypeKeyVerificationMac:
|
||||
case MXEventTypeKeyVerificationDone:
|
||||
case MXEventTypeKeyVerificationCancel:
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
case MXEventTypeRoomMember:
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
case MXEventTypeRoomCreate:
|
||||
shouldAddEvent = NO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [super addEvent:event andRoomState:roomState];
|
||||
if (shouldAddEvent)
|
||||
{
|
||||
shouldAddEvent = [super addEvent:event andRoomState:roomState];
|
||||
}
|
||||
|
||||
return shouldAddEvent;
|
||||
}
|
||||
|
||||
- (void)setKeyVerification:(MXKeyVerification *)keyVerification
|
||||
{
|
||||
_keyVerification = keyVerification;
|
||||
|
||||
[self keyVerificationDidUpdate];
|
||||
}
|
||||
|
||||
- (void)keyVerificationDidUpdate
|
||||
{
|
||||
MXEvent *event = self.getFirstBubbleComponentWithDisplay.event;
|
||||
MXKeyVerification *keyVerification = _keyVerification;
|
||||
|
||||
if (!event)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.eventType)
|
||||
{
|
||||
case MXEventTypeKeyVerificationCancel:
|
||||
{
|
||||
RoomBubbleCellDataTag cellDataTag;
|
||||
|
||||
MXTransactionCancelCode *transactionCancelCode = keyVerification.transaction.reasonCancelCode;
|
||||
|
||||
if (transactionCancelCode
|
||||
&& ([transactionCancelCode isEqual:[MXTransactionCancelCode mismatchedSas]]
|
||||
|| [transactionCancelCode isEqual:[MXTransactionCancelCode mismatchedKeys]]
|
||||
|| [transactionCancelCode isEqual:[MXTransactionCancelCode mismatchedCommitment]]
|
||||
)
|
||||
)
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationConclusion;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationNoDisplay;
|
||||
}
|
||||
|
||||
self.tag = cellDataTag;
|
||||
}
|
||||
break;
|
||||
case MXEventTypeKeyVerificationDone:
|
||||
{
|
||||
RoomBubbleCellDataTag cellDataTag;
|
||||
|
||||
// Avoid to display incoming and outgoing done, only display the incoming one.
|
||||
if (self.isIncoming && keyVerification && (keyVerification.state == MXKeyVerificationStateVerified))
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationConclusion;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationNoDisplay;
|
||||
}
|
||||
|
||||
self.tag = cellDataTag;
|
||||
}
|
||||
break;
|
||||
case MXEventTypeRoomMessage:
|
||||
{
|
||||
NSString *msgType = event.content[@"msgtype"];
|
||||
|
||||
if ([msgType isEqualToString:kMXMessageTypeKeyVerificationRequest])
|
||||
{
|
||||
RoomBubbleCellDataTag cellDataTag;
|
||||
|
||||
if (self.isIncoming && !self.isKeyVerificationOperationPending && keyVerification && keyVerification.state == MXKeyVerificationRequestStatePending)
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellDataTag = RoomBubbleCellDataTagKeyVerificationRequest;
|
||||
}
|
||||
|
||||
self.tag = cellDataTag;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Show all reactions
|
||||
|
||||
|
|
|
@ -103,13 +103,13 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
|||
private func fill(reactionsMenuViewDatas: [ReactionMenuItemViewData]) {
|
||||
self.reactionViewDatas = reactionsMenuViewDatas
|
||||
|
||||
self.reactionsStackView.vc_removeAllSubviews()
|
||||
self.reactionsStackView.vc_removeAllArrangedSubviews()
|
||||
|
||||
let reactionsStackViewCount = self.reactionsStackView.arrangedSubviews.count
|
||||
|
||||
// Remove all menu buttons if reactions count has changed
|
||||
if reactionsStackViewCount != self.reactionViewDatas.count {
|
||||
self.reactionsStackView.vc_removeAllSubviews()
|
||||
self.reactionsStackView.vc_removeAllArrangedSubviews()
|
||||
}
|
||||
|
||||
var index = 0
|
||||
|
|
|
@ -53,7 +53,7 @@ final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoad
|
|||
}
|
||||
|
||||
@objc func fill(contextualMenuItems: [RoomContextualMenuItem]) {
|
||||
self.menuItemsStackView.vc_removeAllSubviews()
|
||||
self.menuItemsStackView.vc_removeAllArrangedSubviews()
|
||||
self.menuItemViews.removeAll()
|
||||
|
||||
for menuItem in contextualMenuItems {
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
|
||||
#import "WidgetManager.h"
|
||||
|
||||
#import "MXRoomSummary+Riot.h"
|
||||
|
||||
@protocol RoomDataSourceDelegate;
|
||||
|
||||
/**
|
||||
The data source for `RoomViewController` in Vector.
|
||||
*/
|
||||
|
@ -39,6 +43,11 @@
|
|||
*/
|
||||
@property(nonatomic) BOOL showBubbleDateTimeOnSelection;
|
||||
|
||||
/**
|
||||
Current room members trust level for an encrypted room.
|
||||
*/
|
||||
@property(nonatomic, readonly) RoomEncryptionTrustLevel encryptionTrustLevel;
|
||||
|
||||
/**
|
||||
Check if there is an active jitsi widget in the room and return it.
|
||||
|
||||
|
@ -62,4 +71,32 @@
|
|||
success:(void (^)(NSString *eventId))success
|
||||
failure:(void (^)(NSError *error))failure;
|
||||
|
||||
/**
|
||||
Accept incoming key verification request.
|
||||
|
||||
@param eventId Event id associated to the key verification request event.
|
||||
@param success A block object called when the operation succeeds.
|
||||
@param failure A block object called when the operation fails.
|
||||
*/
|
||||
- (void)acceptVerificationRequestForEventId:(NSString*)eventId
|
||||
success:(void(^)(void))success
|
||||
failure:(void(^)(NSError*))failure;
|
||||
|
||||
/**
|
||||
Decline incoming key verification request.
|
||||
|
||||
@param eventId Event id associated to the key verification request event.
|
||||
@param success A block object called when the operation succeeds.
|
||||
@param failure A block object called when the operation fails.
|
||||
*/
|
||||
- (void)declineVerificationRequestForEventId:(NSString*)eventId
|
||||
success:(void(^)(void))success
|
||||
failure:(void(^)(NSError*))failure;
|
||||
|
||||
@end
|
||||
|
||||
@protocol RoomDataSourceDelegate <MXKDataSourceDelegate>
|
||||
|
||||
- (void)roomDataSource:(RoomDataSource*)roomDataSource didUpdateEncryptionTrustLevel:(RoomEncryptionTrustLevel)roomEncryptionTrustLevel;
|
||||
|
||||
@end
|
||||
|
|
|
@ -35,6 +35,19 @@
|
|||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
}
|
||||
|
||||
// Observe key verification request changes
|
||||
@property (nonatomic, weak) id keyVerificationRequestDidChangeNotificationObserver;
|
||||
|
||||
// Observe key verification transaction changes
|
||||
@property (nonatomic, weak) id deviceVerificationTransactionDidChangeNotificationObserver;
|
||||
|
||||
// Timer used to debounce cells refresh
|
||||
@property (nonatomic, strong) NSTimer *refreshCellsTimer;
|
||||
|
||||
@property (nonatomic, readonly) id<RoomDataSourceDelegate> roomDataSourceDelegate;
|
||||
|
||||
@property(nonatomic, readwrite) RoomEncryptionTrustLevel encryptionTrustLevel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomDataSource
|
||||
|
@ -71,6 +84,12 @@
|
|||
[self reload];
|
||||
|
||||
}];
|
||||
|
||||
[self registerKeyVerificationRequestNotification];
|
||||
[self registerDeviceVerificationTransactionNotification];
|
||||
[self registerTrustLevelDidChangeNotifications];
|
||||
|
||||
self.encryptionTrustLevel = RoomEncryptionTrustLevelUnknown;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -93,6 +112,21 @@
|
|||
NSLog(@"[MXKRoomDataSource] finalizeRoomDataSource: Cannot retrieve all room members");
|
||||
}];
|
||||
}
|
||||
|
||||
if (self.room.summary.isEncrypted)
|
||||
{
|
||||
[self fetchEncryptionTrustedLevel];
|
||||
}
|
||||
}
|
||||
|
||||
- (id<RoomDataSourceDelegate>)roomDataSourceDelegate
|
||||
{
|
||||
if (!self.delegate || ![self.delegate conformsToProtocol:@protocol(RoomDataSourceDelegate)])
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return ((id<RoomDataSourceDelegate>)(self.delegate));
|
||||
}
|
||||
|
||||
- (void)updateEventFormatter
|
||||
|
@ -117,6 +151,16 @@
|
|||
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
||||
}
|
||||
|
||||
if (self.keyVerificationRequestDidChangeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.keyVerificationRequestDidChangeNotificationObserver];
|
||||
}
|
||||
|
||||
if (self.deviceVerificationTransactionDidChangeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.deviceVerificationTransactionDidChangeNotificationObserver];
|
||||
}
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
|
@ -145,6 +189,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark Encryption trust level
|
||||
|
||||
- (void)registerTrustLevelDidChangeNotifications
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomSummaryDidChange:) name:kMXRoomSummaryDidChangeNotification object:self.room.summary];
|
||||
}
|
||||
|
||||
|
||||
- (void)roomSummaryDidChange:(NSNotification*)notification
|
||||
{
|
||||
if (!self.room.summary.isEncrypted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self fetchEncryptionTrustedLevel];
|
||||
}
|
||||
|
||||
- (void)fetchEncryptionTrustedLevel
|
||||
{
|
||||
self.encryptionTrustLevel = self.room.summary.roomEncryptionTrustLevel;
|
||||
[self.roomDataSourceDelegate roomDataSource:self didUpdateEncryptionTrustLevel:self.encryptionTrustLevel];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
|
@ -190,6 +259,8 @@
|
|||
roomBubbleCellData.senderAvatarPlaceholder = [AvatarGenerator generateAvatarForMatrixItem:roomBubbleCellData.senderId withDisplayName:roomBubbleCellData.senderDisplayName];
|
||||
}
|
||||
|
||||
[self updateKeyVerificationIfNeededForRoomBubbleCellData:roomBubbleCellData];
|
||||
|
||||
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
|
||||
// Finalize cell view customization here
|
||||
|
@ -349,47 +420,57 @@
|
|||
{
|
||||
[bubbleCell.tmpSubviews addObject:avatarsContainer];
|
||||
}
|
||||
[bubbleCell.contentView addSubview:avatarsContainer];
|
||||
|
||||
// Force receipts container size
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:RoomBubbleCellLayout.readReceiptsViewWidth];
|
||||
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:RoomBubbleCellLayout.readReceiptsViewHeight];
|
||||
|
||||
// Force receipts container position
|
||||
NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:avatarsContainer.superview
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1.0
|
||||
constant:-RoomBubbleCellLayout.readReceiptsViewRightMargin];
|
||||
|
||||
// At the bottom, we have reactions or nothing
|
||||
NSLayoutConstraint *topConstraint;
|
||||
if (reactionsView)
|
||||
if ([[bubbleCell class] conformsToProtocol:@protocol(BubbleCellReadReceiptsDisplayable)])
|
||||
{
|
||||
topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:reactionsView.bottomAnchor constant:RoomBubbleCellLayout.readReceiptsViewTopMargin];
|
||||
id<BubbleCellReadReceiptsDisplayable> readReceiptsDisplayable = (id<BubbleCellReadReceiptsDisplayable>)bubbleCell;
|
||||
|
||||
[readReceiptsDisplayable addReadReceiptsView:avatarsContainer];
|
||||
}
|
||||
else
|
||||
{
|
||||
topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:avatarsContainer.superview.topAnchor constant:bottomPositionY + RoomBubbleCellLayout.readReceiptsViewTopMargin];
|
||||
[bubbleCell.contentView addSubview:avatarsContainer];
|
||||
|
||||
// Force receipts container size
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:RoomBubbleCellLayout.readReceiptsViewWidth];
|
||||
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:RoomBubbleCellLayout.readReceiptsViewHeight];
|
||||
|
||||
// Force receipts container position
|
||||
NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:avatarsContainer.superview
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1.0
|
||||
constant:-RoomBubbleCellLayout.readReceiptsViewRightMargin];
|
||||
|
||||
// At the bottom, we have reactions or nothing
|
||||
NSLayoutConstraint *topConstraint;
|
||||
if (reactionsView)
|
||||
{
|
||||
topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:reactionsView.bottomAnchor constant:RoomBubbleCellLayout.readReceiptsViewTopMargin];
|
||||
}
|
||||
else
|
||||
{
|
||||
topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:avatarsContainer.superview.topAnchor constant:bottomPositionY + RoomBubbleCellLayout.readReceiptsViewTopMargin];
|
||||
}
|
||||
|
||||
|
||||
// Available on iOS 8 and later
|
||||
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]];
|
||||
}
|
||||
|
||||
|
||||
// Available on iOS 8 and later
|
||||
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,6 +597,173 @@
|
|||
return cell;
|
||||
}
|
||||
|
||||
- (RoomBubbleCellData*)roomBubbleCellDataForEventId:(NSString*)eventId
|
||||
{
|
||||
id<MXKRoomBubbleCellDataStoring> cellData = [self cellDataOfEventWithEventId:eventId];
|
||||
RoomBubbleCellData *roomBubbleCellData;
|
||||
|
||||
if ([cellData isKindOfClass:RoomBubbleCellData.class])
|
||||
{
|
||||
roomBubbleCellData = (RoomBubbleCellData*)cellData;
|
||||
}
|
||||
|
||||
return roomBubbleCellData;
|
||||
}
|
||||
|
||||
- (MXKeyVerificationRequest*)keyVerificationRequestFromEventId:(NSString*)eventId
|
||||
{
|
||||
RoomBubbleCellData *roomBubbleCellData = [self roomBubbleCellDataForEventId:eventId];
|
||||
|
||||
return roomBubbleCellData.keyVerification.request;
|
||||
}
|
||||
|
||||
- (void)refreshCellsWithDelay
|
||||
{
|
||||
if (self.refreshCellsTimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.refreshCellsTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(refreshCellsTimerFired) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
- (void)refreshCellsTimerFired
|
||||
{
|
||||
[self refreshCells];
|
||||
self.refreshCellsTimer = nil;
|
||||
}
|
||||
|
||||
- (void)refreshCells
|
||||
{
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate dataSource:self didCellChange:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)registerKeyVerificationRequestNotification
|
||||
{
|
||||
self.keyVerificationRequestDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationRequestDidChangeNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification)
|
||||
{
|
||||
id notificationObject = notification.object;
|
||||
|
||||
if ([notificationObject isKindOfClass:MXKeyVerificationByDMRequest.class])
|
||||
{
|
||||
MXKeyVerificationByDMRequest *keyVerificationByDMRequest = (MXKeyVerificationByDMRequest*)notificationObject;
|
||||
|
||||
if ([keyVerificationByDMRequest.roomId isEqualToString:self.roomId])
|
||||
{
|
||||
RoomBubbleCellData *roomBubbleCellData = [self roomBubbleCellDataForEventId:keyVerificationByDMRequest.eventId];
|
||||
|
||||
roomBubbleCellData.isKeyVerificationOperationPending = NO;
|
||||
roomBubbleCellData.keyVerification = nil;
|
||||
|
||||
if (roomBubbleCellData)
|
||||
{
|
||||
[self refreshCellsWithDelay];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)registerDeviceVerificationTransactionNotification
|
||||
{
|
||||
self.deviceVerificationTransactionDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:MXDeviceVerificationTransactionDidChangeNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification)
|
||||
{
|
||||
MXDeviceVerificationTransaction *deviceVerificationTransaction = (MXDeviceVerificationTransaction*)notification.object;
|
||||
|
||||
if ([deviceVerificationTransaction.dmRoomId isEqualToString:self.roomId])
|
||||
{
|
||||
RoomBubbleCellData *roomBubbleCellData = [self roomBubbleCellDataForEventId:deviceVerificationTransaction.dmEventId];
|
||||
|
||||
roomBubbleCellData.isKeyVerificationOperationPending = NO;
|
||||
roomBubbleCellData.keyVerification = nil;
|
||||
|
||||
if (roomBubbleCellData)
|
||||
{
|
||||
[self refreshCellsWithDelay];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)shouldFetchKeyVerificationForEvent:(MXEvent*)event
|
||||
{
|
||||
if (!event)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL shouldFetchKeyVerification = NO;
|
||||
|
||||
switch (event.eventType)
|
||||
{
|
||||
case MXEventTypeKeyVerificationDone:
|
||||
case MXEventTypeKeyVerificationCancel:
|
||||
shouldFetchKeyVerification = YES;
|
||||
break;
|
||||
case MXEventTypeRoomMessage:
|
||||
{
|
||||
NSString *msgType = event.content[@"msgtype"];
|
||||
|
||||
if ([msgType isEqualToString:kMXMessageTypeKeyVerificationRequest])
|
||||
{
|
||||
shouldFetchKeyVerification = YES;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return shouldFetchKeyVerification;
|
||||
}
|
||||
|
||||
- (void)updateKeyVerificationIfNeededForRoomBubbleCellData:(RoomBubbleCellData*)bubbleCellData
|
||||
{
|
||||
MXEvent *event = bubbleCellData.getFirstBubbleComponentWithDisplay.event;
|
||||
|
||||
if (![self shouldFetchKeyVerificationForEvent:event])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bubbleCellData.keyVerification != nil || bubbleCellData.isKeyVerificationOperationPending)
|
||||
{
|
||||
// Key verification already fetched or request is pending do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
__block MXHTTPOperation *operation = [self.mxSession.crypto.deviceVerificationManager keyVerificationFromKeyVerificationEvent:event
|
||||
success:^(MXKeyVerification * _Nonnull keyVerification)
|
||||
{
|
||||
BOOL shouldRefreshCells = bubbleCellData.isKeyVerificationOperationPending || bubbleCellData.keyVerification == nil;
|
||||
|
||||
bubbleCellData.keyVerification = keyVerification;
|
||||
bubbleCellData.isKeyVerificationOperationPending = NO;
|
||||
|
||||
if (shouldRefreshCells)
|
||||
{
|
||||
[self refreshCellsWithDelay];
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
NSLog(@"[RoomDataSource] updateKeyVerificationIfNeededForRoomBubbleCellData; keyVerificationFromKeyVerificationEvent fails with error: %@", error);
|
||||
|
||||
bubbleCellData.isKeyVerificationOperationPending = NO;
|
||||
}];
|
||||
|
||||
bubbleCellData.isKeyVerificationOperationPending = !operation;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setSelectedEventId:(NSString *)selectedEventId
|
||||
|
@ -567,6 +815,68 @@
|
|||
[self sendVideo:videoLocalURL withThumbnail:videoThumbnail success:success failure:failure];
|
||||
}
|
||||
|
||||
- (void)acceptVerificationRequestForEventId:(NSString*)eventId success:(void(^)(void))success failure:(void(^)(NSError*))failure
|
||||
{
|
||||
MXKeyVerificationRequest *keyVerificationRequest = [self keyVerificationRequestFromEventId:eventId];
|
||||
|
||||
if (!keyVerificationRequest)
|
||||
{
|
||||
NSError *error;
|
||||
|
||||
if (failure)
|
||||
{
|
||||
failure(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[[AppDelegate theDelegate] presentIncomingKeyVerificationRequest:keyVerificationRequest inSession:self.mxSession];
|
||||
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)declineVerificationRequestForEventId:(NSString*)eventId success:(void(^)(void))success failure:(void(^)(NSError*))failure
|
||||
{
|
||||
MXKeyVerificationRequest *keyVerificationRequest = [self keyVerificationRequestFromEventId:eventId];
|
||||
|
||||
if (!keyVerificationRequest)
|
||||
{
|
||||
NSError *error;
|
||||
|
||||
if (failure)
|
||||
{
|
||||
failure(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RoomBubbleCellData *roomBubbleCellData = [self roomBubbleCellDataForEventId:eventId];
|
||||
roomBubbleCellData.isKeyVerificationOperationPending = YES;
|
||||
|
||||
[self refreshCells];
|
||||
|
||||
[keyVerificationRequest cancelWithCancelCode:MXTransactionCancelCode.user success:^{
|
||||
|
||||
// roomBubbleCellData.isKeyVerificationOperationPending will be set to NO by MXKeyVerificationRequestDidChangeNotification notification
|
||||
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
roomBubbleCellData.isKeyVerificationOperationPending = NO;
|
||||
|
||||
if (failure)
|
||||
{
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma - Accessibility
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -28,15 +28,19 @@
|
|||
|
||||
#import "TableViewCellWithButton.h"
|
||||
#import "RoomTableViewCell.h"
|
||||
#import "MXRoom+Riot.h"
|
||||
|
||||
#define TABLEVIEW_ROW_CELL_HEIGHT 46
|
||||
#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;
|
||||
NSArray<NSNumber*> *securityActionsArray;
|
||||
|
||||
/**
|
||||
List of the admin actions on this member.
|
||||
*/
|
||||
|
@ -78,6 +82,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 +129,7 @@
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
self.encryptionTrustLevel = UserEncryptionTrustLevelUnknown;
|
||||
|
||||
adminActionsArray = [[NSMutableArray alloc] init];
|
||||
otherActionsArray = [[NSMutableArray alloc] init];
|
||||
|
@ -195,10 +220,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 +292,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 +414,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 +439,103 @@
|
|||
}
|
||||
|
||||
// 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) {
|
||||
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];
|
||||
}
|
||||
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) {
|
||||
} 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];
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
self.encryptionTrustLevel = [self.mxRoom encryptionTrustLevelForUserId:userId];
|
||||
[self updateMemberInfo];
|
||||
}
|
||||
|
||||
- (UIImage*)userEncryptionBadgeImage
|
||||
{
|
||||
NSString *encryptionIconName;
|
||||
UIImage *encryptionIcon;
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel = self.encryptionTrustLevel;
|
||||
|
||||
switch (userEncryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
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 +584,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 +718,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
if (RiotSettings.shared.enableCrossSigning)
|
||||
{
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
securityActionsArray = @[@(MXKRoomMemberDetailsActionSecurityInformation)];
|
||||
break;
|
||||
case UserEncryptionTrustLevelNone:
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
securityActionsArray = @[@(MXKRoomMemberDetailsActionSecurity),
|
||||
@(MXKRoomMemberDetailsActionSecurityInformation)];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
|
||||
|
||||
if (securityActionsArray.count)
|
||||
{
|
||||
securityIndex = sectionCount++;
|
||||
}
|
||||
|
||||
if (otherActionsArray.count)
|
||||
{
|
||||
|
@ -644,7 +768,11 @@
|
|||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
return securityActionsArray.count;
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return adminActionsArray.count;
|
||||
}
|
||||
|
@ -666,10 +794,18 @@
|
|||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_security", @"Vector", nil);
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_admin_tools", @"Vector", nil);
|
||||
}
|
||||
else if (RiotSettings.shared.enableCrossSigning && section == otherActionsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_other", @"Vector", nil);
|
||||
}
|
||||
else if (section == directChatsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_direct_chats", @"Vector", nil);
|
||||
|
@ -741,7 +877,87 @@
|
|||
{
|
||||
UITableViewCell *cell;
|
||||
|
||||
if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
if (indexPath.section == securityIndex && indexPath.row < securityActionsArray.count)
|
||||
{
|
||||
NSNumber *actionNumber = securityActionsArray[indexPath.row];
|
||||
|
||||
if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionSecurity)
|
||||
{
|
||||
MXKTableViewCell *securityStatusCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSString *statusText;
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
statusText = NSLocalizedStringFromTable(@"room_participants_action_security_status_verified", @"Vector", nil);
|
||||
break;
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
statusText = NSLocalizedStringFromTable(@"room_participants_action_security_status_verify", @"Vector", nil);
|
||||
break;
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
statusText = NSLocalizedStringFromTable(@"room_participants_action_security_status_warning", @"Vector", nil);
|
||||
break;
|
||||
default:
|
||||
statusText = NSLocalizedStringFromTable(@"room_participants_action_security_status_loading", @"Vector", nil);
|
||||
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 (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionSecurityInformation)
|
||||
{
|
||||
MXKTableViewCell *encryptionInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSMutableString *encryptionInformation = [NSMutableString new];
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
[encryptionInformation appendString:NSLocalizedStringFromTable(@"room_participants_security_information_room_encrypted", @"Vector", nil)];
|
||||
break;
|
||||
case UserEncryptionTrustLevelNone:
|
||||
[encryptionInformation appendString:NSLocalizedStringFromTable(@"room_participants_security_information_room_not_encrypted", @"Vector", nil)];
|
||||
break;
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
[encryptionInformation appendString:NSLocalizedStringFromTable(@"room_participants_security_information_loading", @"Vector", nil)];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionInformation.length)
|
||||
{
|
||||
[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 if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
{
|
||||
TableViewCellWithButton *cellWithButton = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
|
@ -852,26 +1068,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 +1080,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
|
@ -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
|
||||
};
|
|
@ -125,7 +125,8 @@
|
|||
|
||||
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, UIScrollViewAccessibilityDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
|
||||
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate, EmojiPickerCoordinatorBridgePresenterDelegate,
|
||||
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate>
|
||||
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate,
|
||||
RoomDataSourceDelegate>
|
||||
{
|
||||
// The expanded header
|
||||
ExpandedRoomTitleView *expandedHeader;
|
||||
|
@ -353,6 +354,14 @@
|
|||
[self.bubblesTableView registerClass:RoomSelectedStickerBubbleCell.class forCellReuseIdentifier:RoomSelectedStickerBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:RoomPredecessorBubbleCell.class forCellReuseIdentifier:RoomPredecessorBubbleCell.defaultReuseIdentifier];
|
||||
|
||||
[self.bubblesTableView registerClass:KeyVerificationIncomingRequestApprovalBubbleCell.class forCellReuseIdentifier:KeyVerificationIncomingRequestApprovalBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.class forCellReuseIdentifier:KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:KeyVerificationRequestStatusBubbleCell.class forCellReuseIdentifier:KeyVerificationRequestStatusBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:KeyVerificationRequestStatusWithPaginationTitleBubbleCell.class forCellReuseIdentifier:KeyVerificationRequestStatusWithPaginationTitleBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:KeyVerificationConclusionBubbleCell.class forCellReuseIdentifier:KeyVerificationConclusionBubbleCell.defaultReuseIdentifier];
|
||||
[self.bubblesTableView registerClass:KeyVerificationConclusionWithPaginationTitleBubbleCell.class forCellReuseIdentifier:KeyVerificationConclusionWithPaginationTitleBubbleCell.defaultReuseIdentifier];
|
||||
|
||||
|
||||
// Prepare expanded header
|
||||
expandedHeader = [ExpandedRoomTitleView roomTitleView];
|
||||
expandedHeader.delegate = self;
|
||||
|
@ -1413,6 +1422,11 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isEncryptionEnabled
|
||||
{
|
||||
return self.roomDataSource.room.summary.isEncrypted && self.mainSession.crypto != nil;
|
||||
}
|
||||
|
||||
- (void)refreshRoomTitle
|
||||
{
|
||||
if (rightBarButtonItems && !self.navigationItem.rightBarButtonItems)
|
||||
|
@ -1533,12 +1547,8 @@
|
|||
roomInputToolbarView.supportCallOption &= ([[AppDelegate theDelegate] callStatusBarWindow] == nil);
|
||||
}
|
||||
|
||||
// Check whether the encryption is enabled in the room
|
||||
if (self.roomDataSource.room.summary.isEncrypted)
|
||||
{
|
||||
// Encrypt the user's messages as soon as the user supports the encryption?
|
||||
roomInputToolbarView.isEncryptionEnabled = (self.mainSession.crypto != nil);
|
||||
}
|
||||
// Update encryption decoration if needed
|
||||
[self updateEncryptionDecorationForRoomInputToolbar:roomInputToolbarView];
|
||||
}
|
||||
else if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class])
|
||||
{
|
||||
|
@ -1618,6 +1628,64 @@
|
|||
[UIView setAnimationsEnabled:YES];
|
||||
}
|
||||
|
||||
- (UIImage*)roomEncryptionBadgeImage
|
||||
{
|
||||
NSString *encryptionIconName;
|
||||
UIImage *encryptionIcon;
|
||||
|
||||
if (self.isEncryptionEnabled)
|
||||
{
|
||||
RoomEncryptionTrustLevel roomEncryptionTrustLevel = ((RoomDataSource*)self.roomDataSource).encryptionTrustLevel;
|
||||
|
||||
switch (roomEncryptionTrustLevel) {
|
||||
case RoomEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelUnknown:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
encryptionIcon = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
|
||||
return encryptionIcon;
|
||||
}
|
||||
|
||||
- (void)updateInputToolbarEncryptionDecoration
|
||||
{
|
||||
if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
|
||||
{
|
||||
RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView;
|
||||
[self updateEncryptionDecorationForRoomInputToolbar:roomInputToolbarView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateExpandedHeaderEncryptionDecoration
|
||||
{
|
||||
if (self->expandedHeader)
|
||||
{
|
||||
self->expandedHeader.roomAvatarBadgeImageView.image = self.roomEncryptionBadgeImage;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateEncryptionDecorationForRoomInputToolbar:(RoomInputToolbarView*)roomInputToolbarView
|
||||
{
|
||||
roomInputToolbarView.isEncryptionEnabled = self.isEncryptionEnabled;
|
||||
roomInputToolbarView.encryptedRoomIcon.image = self.roomEncryptionBadgeImage;
|
||||
}
|
||||
|
||||
- (void)handleLongPressFromCell:(id<MXKCellRendering>)cell withTappedEvent:(MXEvent*)event
|
||||
{
|
||||
if (event && !customizedRoomDataSource.selectedEventId)
|
||||
|
@ -1717,9 +1785,12 @@
|
|||
// Note the avatar title view does not define tap gesture.
|
||||
|
||||
expandedHeader.roomAvatar.alpha = 0.0;
|
||||
expandedHeader.roomAvatarBadgeImageView.alpha = 0.0;
|
||||
|
||||
shadowImage = [[UIImage alloc] init];
|
||||
|
||||
[self updateExpandedHeaderEncryptionDecoration];
|
||||
|
||||
// Dismiss the keyboard when header is expanded.
|
||||
[self.inputToolbarView dismissKeyboard];
|
||||
}
|
||||
|
@ -1750,7 +1821,8 @@
|
|||
self.bubblesTableViewTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top : 0);
|
||||
self.jumpToLastUnreadBannerContainerTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant : self.bubblesTableView.mxk_adjustedContentInset.top);
|
||||
|
||||
expandedHeader.roomAvatar.alpha = 1;
|
||||
self->expandedHeader.roomAvatar.alpha = 1;
|
||||
self->expandedHeader.roomAvatarBadgeImageView.alpha = 1;
|
||||
|
||||
// Force to render the view
|
||||
[self forceLayoutRefresh];
|
||||
|
@ -2039,6 +2111,18 @@
|
|||
{
|
||||
cellViewClass = RoomPredecessorBubbleCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval)
|
||||
{
|
||||
cellViewClass = bubbleData.isPaginationFirstBubble ? KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.class : KeyVerificationIncomingRequestApprovalBubbleCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagKeyVerificationRequest)
|
||||
{
|
||||
cellViewClass = bubbleData.isPaginationFirstBubble ? KeyVerificationRequestStatusWithPaginationTitleBubbleCell.class : KeyVerificationRequestStatusBubbleCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagKeyVerificationConclusion)
|
||||
{
|
||||
cellViewClass = bubbleData.isPaginationFirstBubble ? KeyVerificationConclusionWithPaginationTitleBubbleCell.class : KeyVerificationConclusionBubbleCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagMembership)
|
||||
{
|
||||
if (bubbleData.collapsed)
|
||||
|
@ -2255,6 +2339,30 @@
|
|||
[self showContextualMenuForEvent:selectedEvent fromSingleTapGesture:YES cell:cell animated:YES];
|
||||
}
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed])
|
||||
{
|
||||
NSString *eventId = userInfo[kMXKRoomBubbleCellEventIdKey];
|
||||
|
||||
RoomDataSource *roomDataSource = (RoomDataSource*)self.roomDataSource;
|
||||
|
||||
[roomDataSource acceptVerificationRequestForEventId:eventId success:^{
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed])
|
||||
{
|
||||
NSString *eventId = userInfo[kMXKRoomBubbleCellEventIdKey];
|
||||
|
||||
RoomDataSource *roomDataSource = (RoomDataSource*)self.roomDataSource;
|
||||
|
||||
[roomDataSource declineVerificationRequestForEventId:eventId success:^{
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView])
|
||||
{
|
||||
if (((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventSentState == MXEventSentStateFailed)
|
||||
|
@ -3149,6 +3257,14 @@
|
|||
return roomInputToolbarView;
|
||||
}
|
||||
|
||||
#pragma mark - RoomDataSourceDelegate
|
||||
|
||||
- (void)roomDataSource:(RoomDataSource *)roomDataSource didUpdateEncryptionTrustLevel:(RoomEncryptionTrustLevel)roomEncryptionTrustLevel
|
||||
{
|
||||
[self updateInputToolbarEncryptionDecoration];
|
||||
[self updateExpandedHeaderEncryptionDecoration];
|
||||
}
|
||||
|
||||
#pragma mark - Segues
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
|
@ -5303,8 +5419,36 @@
|
|||
|
||||
// Copy action
|
||||
|
||||
BOOL isCopyActionEnabled = !attachment || attachment.type != MXKAttachmentTypeSticker;
|
||||
|
||||
if (isCopyActionEnabled)
|
||||
{
|
||||
switch (event.eventType) {
|
||||
case MXEventTypeRoomMessage:
|
||||
{
|
||||
NSString *messageType = event.content[@"msgtype"];
|
||||
|
||||
if ([messageType isEqualToString:kMXMessageTypeKeyVerificationRequest])
|
||||
{
|
||||
isCopyActionEnabled = NO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MXEventTypeKeyVerificationStart:
|
||||
case MXEventTypeKeyVerificationAccept:
|
||||
case MXEventTypeKeyVerificationKey:
|
||||
case MXEventTypeKeyVerificationMac:
|
||||
case MXEventTypeKeyVerificationDone:
|
||||
case MXEventTypeKeyVerificationCancel:
|
||||
isCopyActionEnabled = NO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RoomContextualMenuItem *copyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionCopy];
|
||||
copyMenuItem.isEnabled = !attachment || attachment.type != MXKAttachmentTypeSticker;
|
||||
copyMenuItem.isEnabled = isCopyActionEnabled;
|
||||
copyMenuItem.action = ^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
@objcMembers
|
||||
final class BubbleCellContentView: UIView, NibLoadable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet weak var bubbleInfoContainer: UIView!
|
||||
@IBOutlet weak var bubbleInfoContainerTopConstraint: NSLayoutConstraint!
|
||||
|
||||
@IBOutlet weak var innerContentView: UIView!
|
||||
|
||||
@IBOutlet weak var readReceiptsContainerView: UIView!
|
||||
@IBOutlet weak var readReceiptsContentView: UIView!
|
||||
|
||||
@IBOutlet weak var bubbleOverlayContainer: UIView!
|
||||
|
||||
@IBOutlet weak var paginationTitleContainerView: UIView!
|
||||
@IBOutlet weak var paginationLabel: UILabel!
|
||||
@IBOutlet weak var paginationSeparatorView: UIView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var showReadReceipts: Bool {
|
||||
get {
|
||||
return !self.readReceiptsContainerView.isHidden
|
||||
}
|
||||
set {
|
||||
self.readReceiptsContainerView.isHidden = !newValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var showPaginationTitle: Bool {
|
||||
get {
|
||||
return !self.paginationTitleContainerView.isHidden
|
||||
}
|
||||
set {
|
||||
self.paginationTitleContainerView.isHidden = !newValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate() -> BubbleCellContentView {
|
||||
return BubbleCellContentView.loadFromNib()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.paginationLabel.textColor = theme.tintColor
|
||||
self.paginationSeparatorView.backgroundColor = theme.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BubbleCellReadReceiptsDisplayable
|
||||
extension BubbleCellContentView: BubbleCellReadReceiptsDisplayable {
|
||||
|
||||
func addReadReceiptsView(_ readReceiptsView: UIView) {
|
||||
self.readReceiptsContentView.vc_removeAllSubviews()
|
||||
self.readReceiptsContentView.vc_addSubViewMatchingParent(readReceiptsView)
|
||||
self.showReadReceipts = true
|
||||
}
|
||||
|
||||
func removeReadReceiptsView() {
|
||||
self.showReadReceipts = false
|
||||
self.readReceiptsContentView.vc_removeAllSubviews()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
<?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" 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="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"/>
|
||||
<view contentMode="scaleToFill" id="zG5-YA-Ijy" customClass="BubbleCellContentView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XQw-Mj-NZY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="5GX-gn-bK1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="u1e-Q2-PhY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="54"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ro1-vP-6Ha" userLabel="Pagination Title View">
|
||||
<rect key="frame" x="67" y="10" width="518" height="24"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="r7y-FK-GWS" userLabel="Pagination Label">
|
||||
<rect key="frame" x="0.0" y="0.0" width="508" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="uCj-An-Yc2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<color key="textColor" red="0.66666666669999997" green="0.66666666669999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ytv-tA-NmI" userLabel="Pagination Separator View">
|
||||
<rect key="frame" x="0.0" y="23" width="518" height="1"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="PaginationTitleView"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="5bk-I2-Cw6"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ytv-tA-NmI" secondAttribute="bottom" id="5oD-hl-YNI"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ytv-tA-NmI" secondAttribute="trailing" id="Cfu-Jn-urV"/>
|
||||
<constraint firstItem="r7y-FK-GWS" firstAttribute="leading" secondItem="Ro1-vP-6Ha" secondAttribute="leading" id="Fpo-9J-9Ci"/>
|
||||
<constraint firstItem="r7y-FK-GWS" firstAttribute="top" secondItem="Ro1-vP-6Ha" secondAttribute="top" id="Rpc-oi-muy"/>
|
||||
<constraint firstItem="ytv-tA-NmI" firstAttribute="top" secondItem="r7y-FK-GWS" secondAttribute="bottom" constant="5" id="lam-eF-rEV"/>
|
||||
<constraint firstItem="ytv-tA-NmI" firstAttribute="leading" secondItem="Ro1-vP-6Ha" secondAttribute="leading" id="oBH-4x-jgt"/>
|
||||
<constraint firstAttribute="trailing" secondItem="r7y-FK-GWS" secondAttribute="trailing" constant="10" id="r4y-XW-aAD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Ro1-vP-6Ha" firstAttribute="top" secondItem="u1e-Q2-PhY" secondAttribute="top" constant="10" id="5kZ-rt-pvq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ro1-vP-6Ha" secondAttribute="trailing" constant="10" id="8P7-ZL-7pg"/>
|
||||
<constraint firstItem="Ro1-vP-6Ha" firstAttribute="leading" secondItem="u1e-Q2-PhY" secondAttribute="leading" constant="67" id="NCS-nK-fLb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ro1-vP-6Ha" secondAttribute="bottom" constant="20" id="UcW-P4-rQv"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vcq-cR-uBc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="oeI-eO-mFK">
|
||||
<rect key="frame" x="56" y="3" width="474" height="91"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Y6-Py-paB">
|
||||
<rect key="frame" x="530" y="3" width="50" height="91"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="BubbleInfoContainer"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="50" id="LtA-zk-OCc"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="oeI-eO-mFK" firstAttribute="leading" secondItem="vcq-cR-uBc" secondAttribute="leading" constant="56" id="0Fr-0L-9tU"/>
|
||||
<constraint firstItem="7Y6-Py-paB" firstAttribute="top" secondItem="vcq-cR-uBc" secondAttribute="top" constant="3" id="16j-F9-tL8"/>
|
||||
<constraint firstAttribute="bottom" secondItem="oeI-eO-mFK" secondAttribute="bottom" constant="3" id="8M5-uW-82s"/>
|
||||
<constraint firstItem="7Y6-Py-paB" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="trailing" id="9V6-A8-9i0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7Y6-Py-paB" secondAttribute="bottom" constant="3" id="lee-yN-381"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7Y6-Py-paB" secondAttribute="trailing" constant="15" id="rG0-0L-I4O"/>
|
||||
<constraint firstItem="oeI-eO-mFK" firstAttribute="top" secondItem="vcq-cR-uBc" secondAttribute="top" constant="3" id="uZZ-I6-Xtq"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4zo-V8-CNe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="595" height="12"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rQt-NH-Cb0">
|
||||
<rect key="frame" x="439" y="0.0" width="150" height="12"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="150" id="fsY-DK-hUg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="rQt-NH-Cb0" secondAttribute="bottom" id="6yV-vn-doj"/>
|
||||
<constraint firstItem="rQt-NH-Cb0" firstAttribute="top" secondItem="4zo-V8-CNe" secondAttribute="top" id="DNc-jy-Urh"/>
|
||||
<constraint firstAttribute="height" constant="12" id="TxZ-dJ-UI6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rQt-NH-Cb0" secondAttribute="trailing" constant="6" id="lq2-AY-Lus"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="u1e-Q2-PhY" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="KrJ-dm-TaV"/>
|
||||
<constraint firstItem="4zo-V8-CNe" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="bdq-sQ-NQy"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.95294117649999999" green="0.97254901959999995" blue="0.99215686270000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="5GX-gn-bK1" firstAttribute="leading" secondItem="zG5-YA-Ijy" secondAttribute="leading" id="36u-ut-l3G"/>
|
||||
<constraint firstItem="5GX-gn-bK1" firstAttribute="top" secondItem="zG5-YA-Ijy" secondAttribute="top" id="As1-dI-9ba"/>
|
||||
<constraint firstItem="XQw-Mj-NZY" firstAttribute="top" secondItem="zG5-YA-Ijy" secondAttribute="top" id="BuL-ri-8kT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5GX-gn-bK1" secondAttribute="bottom" id="cNV-Or-YPg"/>
|
||||
<constraint firstAttribute="trailing" secondItem="5GX-gn-bK1" secondAttribute="trailing" id="deR-Cu-Brh"/>
|
||||
<constraint firstAttribute="trailing" secondItem="XQw-Mj-NZY" secondAttribute="trailing" id="eJl-Fg-neU"/>
|
||||
<constraint firstAttribute="bottom" secondItem="XQw-Mj-NZY" secondAttribute="bottom" id="kPs-G8-HdC"/>
|
||||
<constraint firstItem="XQw-Mj-NZY" firstAttribute="leading" secondItem="zG5-YA-Ijy" secondAttribute="leading" id="w8M-8g-ZQD"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="bubbleInfoContainer" destination="7Y6-Py-paB" id="uLv-MM-HIL"/>
|
||||
<outlet property="bubbleInfoContainerTopConstraint" destination="16j-F9-tL8" id="zxd-pd-SSx"/>
|
||||
<outlet property="bubbleOverlayContainer" destination="XQw-Mj-NZY" id="6d1-EN-LPY"/>
|
||||
<outlet property="innerContentView" destination="oeI-eO-mFK" id="ap1-He-C6g"/>
|
||||
<outlet property="paginationLabel" destination="r7y-FK-GWS" id="R9V-ix-mDu"/>
|
||||
<outlet property="paginationSeparatorView" destination="ytv-tA-NmI" id="sgk-n1-KQi"/>
|
||||
<outlet property="paginationTitleContainerView" destination="u1e-Q2-PhY" id="Osl-dF-fpA"/>
|
||||
<outlet property="readReceiptsContainerView" destination="4zo-V8-CNe" id="7ek-u4-CX8"/>
|
||||
<outlet property="readReceiptsContentView" destination="rQt-NH-Cb0" id="tqw-je-kp9"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-975" y="-1318"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc protocol BubbleCellReadReceiptsDisplayable {
|
||||
func addReadReceiptsView(_ readReceiptsView: UIView)
|
||||
func removeReadReceiptsView()
|
||||
}
|
|
@ -23,60 +23,51 @@ NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon = @"kRoomEncrypt
|
|||
|
||||
+ (UIImage*)encryptionIconForEvent:(MXEvent*)event andSession:(MXSession*)session
|
||||
{
|
||||
NSString *encryptionIcon;
|
||||
MXRoom *room = [session roomWithRoomId:event.roomId];
|
||||
BOOL isRoomEncrypted = room.summary.isEncrypted && session.crypto;
|
||||
|
||||
if (!isRoomEncrypted)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *encryptionIconName;
|
||||
UIImage* encryptionIcon;
|
||||
|
||||
if (!event.isEncrypted)
|
||||
{
|
||||
encryptionIcon = @"e2e_unencrypted";
|
||||
|
||||
if (event.isLocalEvent
|
||||
|| event.isState
|
||||
|| event.contentHasBeenEdited) // Local echo for an edit is clear but uses a true event id, the one of the edited event
|
||||
{
|
||||
// Patch: Display the verified icon by default on pending outgoing messages in the encrypted rooms when the encryption is enabled
|
||||
MXRoom *room = [session roomWithRoomId:event.roomId];
|
||||
if (room.summary.isEncrypted && session.crypto)
|
||||
{
|
||||
// The outgoing message are encrypted by default
|
||||
encryptionIcon = @"e2e_verified";
|
||||
}
|
||||
encryptionIconName = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptionIconName = @"encryption_warning";
|
||||
}
|
||||
}
|
||||
else if (event.decryptionError)
|
||||
{
|
||||
encryptionIcon = @"e2e_blocked";
|
||||
encryptionIconName = @"encryption_warning";
|
||||
}
|
||||
else
|
||||
else if (event.sender)
|
||||
{
|
||||
MXUserTrustLevel *userTrustLevel = [session.crypto trustLevelForUser:event.sender];
|
||||
MXDeviceInfo *deviceInfo = [session.crypto eventDeviceInfo:event];
|
||||
|
||||
if (deviceInfo)
|
||||
if (userTrustLevel.isVerified && !deviceInfo.trustLevel.isVerified)
|
||||
{
|
||||
switch (deviceInfo.verified)
|
||||
{
|
||||
case MXDeviceUnknown:
|
||||
case MXDeviceUnverified:
|
||||
{
|
||||
encryptionIcon = @"e2e_warning";
|
||||
break;
|
||||
}
|
||||
case MXDeviceVerified:
|
||||
{
|
||||
encryptionIcon = @"e2e_verified";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
encryptionIconName = @"encryption_warning";
|
||||
}
|
||||
}
|
||||
|
||||
if (!encryptionIcon)
|
||||
if (encryptionIconName)
|
||||
{
|
||||
// Use the warning icon by default
|
||||
encryptionIcon = @"e2e_warning";
|
||||
encryptionIcon = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
|
||||
return [UIImage imageNamed:encryptionIcon];
|
||||
return encryptionIcon;
|
||||
}
|
||||
|
||||
+ (void)addEncryptionStatusFromBubbleData:(MXKRoomBubbleCellData *)bubbleData inContainerView:(UIView *)containerView
|
||||
|
@ -104,19 +95,23 @@ NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon = @"kRoomEncrypt
|
|||
}
|
||||
|
||||
UIImage *icon = [RoomEncryptedDataBubbleCell encryptionIconForEvent:component.event andSession:bubbleData.mxSession];
|
||||
UIImageView *encryptStatusImageView = [[UIImageView alloc] initWithImage:icon];
|
||||
|
||||
CGRect frame = encryptStatusImageView.frame;
|
||||
frame.origin.y = component.position.y + 3;
|
||||
encryptStatusImageView.frame = frame;
|
||||
if (icon)
|
||||
{
|
||||
UIImageView *encryptStatusImageView = [[UIImageView alloc] initWithImage:icon];
|
||||
|
||||
CGPoint center = encryptStatusImageView.center;
|
||||
center.x = containerView.frame.size.width / 2;
|
||||
encryptStatusImageView.center = center;
|
||||
CGRect frame = encryptStatusImageView.frame;
|
||||
frame.origin.y = component.position.y + 3;
|
||||
encryptStatusImageView.frame = frame;
|
||||
|
||||
encryptStatusImageView.tag = componentIndex;
|
||||
CGPoint center = encryptStatusImageView.center;
|
||||
center.x = containerView.frame.size.width / 2;
|
||||
encryptStatusImageView.center = center;
|
||||
|
||||
[containerView addSubview:encryptStatusImageView];
|
||||
encryptStatusImageView.tag = componentIndex;
|
||||
|
||||
[containerView addSubview:encryptStatusImageView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static var sizes = Set<SizingViewHeight>()
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var keyVerificationCellInnerContentView: KeyVerificationCellInnerContentView?
|
||||
|
||||
weak var bubbleCellContentView: BubbleCellContentView?
|
||||
|
||||
override var bubbleInfoContainer: UIView! {
|
||||
get {
|
||||
guard let infoContainer = self.bubbleCellContentView?.bubbleInfoContainer else {
|
||||
fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainer should not be used before set")
|
||||
}
|
||||
return infoContainer
|
||||
}
|
||||
set {
|
||||
super.bubbleInfoContainer = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override var bubbleOverlayContainer: UIView! {
|
||||
get {
|
||||
guard let overlayContainer = self.bubbleCellContentView?.bubbleOverlayContainer else {
|
||||
fatalError("[KeyVerificationBaseBubbleCell] bubbleOverlayContainer should not be used before set")
|
||||
}
|
||||
return overlayContainer
|
||||
}
|
||||
set {
|
||||
super.bubbleInfoContainer = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override var bubbleInfoContainerTopConstraint: NSLayoutConstraint! {
|
||||
get {
|
||||
guard let infoContainerTopConstraint = self.bubbleCellContentView?.bubbleInfoContainerTopConstraint else {
|
||||
fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainerTopConstraint should not be used before set")
|
||||
}
|
||||
return infoContainerTopConstraint
|
||||
}
|
||||
set {
|
||||
super.bubbleInfoContainerTopConstraint = newValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
|
||||
self.selectionStyle = .none
|
||||
self.setupContentView()
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
|
||||
super.setupViews()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.bubbleCellContentView?.update(theme: theme)
|
||||
self.keyVerificationCellInnerContentView?.update(theme: theme)
|
||||
}
|
||||
|
||||
func buildUserInfoText(with userId: String, userDisplayName: String?) -> String {
|
||||
|
||||
let userInfoText: String
|
||||
|
||||
if let userDisplayName = userDisplayName {
|
||||
userInfoText = "\(userId) (\(userDisplayName))"
|
||||
} else {
|
||||
userInfoText = userId
|
||||
}
|
||||
|
||||
return userInfoText
|
||||
}
|
||||
|
||||
func senderId(from bubbleCellData: MXKRoomBubbleCellData) -> String {
|
||||
return bubbleCellData.senderId ?? ""
|
||||
}
|
||||
|
||||
func senderDisplayName(from bubbleCellData: MXKRoomBubbleCellData) -> String? {
|
||||
let senderId = self.senderId(from: bubbleCellData)
|
||||
guard let senderDisplayName = bubbleCellData.senderDisplayName, senderId != senderDisplayName else {
|
||||
return nil
|
||||
}
|
||||
return senderDisplayName
|
||||
}
|
||||
|
||||
class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
fatalError("[KeyVerificationBaseBubbleCell] Subclass should implement this method")
|
||||
}
|
||||
|
||||
class func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int {
|
||||
|
||||
var hasher = Hasher()
|
||||
|
||||
let sizingView = self.sizingView()
|
||||
sizingView.render(bubbleCellData)
|
||||
|
||||
// Add cell class name
|
||||
hasher.combine(self.defaultReuseIdentifier())
|
||||
|
||||
if let keyVerificationCellInnerContentView = sizingView.keyVerificationCellInnerContentView {
|
||||
|
||||
// Add other user info
|
||||
if let otherUserInfo = keyVerificationCellInnerContentView.otherUserInfo {
|
||||
hasher.combine(otherUserInfo)
|
||||
}
|
||||
|
||||
// Add request status text
|
||||
if keyVerificationCellInnerContentView.isRequestStatusHidden == false,
|
||||
let requestStatusText = sizingView.keyVerificationCellInnerContentView?.requestStatusText {
|
||||
hasher.combine(requestStatusText)
|
||||
}
|
||||
}
|
||||
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override class func defaultReuseIdentifier() -> String! {
|
||||
return String(describing: self)
|
||||
}
|
||||
|
||||
override func didEndDisplay() {
|
||||
super.didEndDisplay()
|
||||
self.removeReadReceiptsView()
|
||||
}
|
||||
|
||||
override class func height(for cellData: MXKCellData!, withMaximumWidth maxWidth: CGFloat) -> CGFloat {
|
||||
guard let cellData = cellData else {
|
||||
return 0
|
||||
}
|
||||
|
||||
guard let roomBubbleCellData = cellData as? MXKRoomBubbleCellData else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let height: CGFloat
|
||||
|
||||
let sizingViewHeight = self.findOrCreateSizingViewHeight(from: roomBubbleCellData)
|
||||
|
||||
if let cachedHeight = sizingViewHeight.heights[maxWidth] {
|
||||
height = cachedHeight
|
||||
} else {
|
||||
height = self.contentViewHeight(for: roomBubbleCellData, fitting: maxWidth)
|
||||
sizingViewHeight.heights[maxWidth] = height
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
override func render(_ cellData: MXKCellData!) {
|
||||
super.render(cellData)
|
||||
|
||||
if let bubbleData = self.bubbleData,
|
||||
let bubbleCellContentView = self.bubbleCellContentView,
|
||||
let paginationDate = bubbleData.date,
|
||||
bubbleCellContentView.showPaginationTitle {
|
||||
bubbleCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupContentView() {
|
||||
if self.bubbleCellContentView == nil {
|
||||
|
||||
let bubbleCellContentView = BubbleCellContentView.instantiate()
|
||||
|
||||
let innerContentView = KeyVerificationCellInnerContentView.instantiate()
|
||||
|
||||
bubbleCellContentView.innerContentView.vc_addSubViewMatchingParent(innerContentView)
|
||||
|
||||
self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView)
|
||||
|
||||
self.bubbleCellContentView = bubbleCellContentView
|
||||
self.keyVerificationCellInnerContentView = innerContentView
|
||||
}
|
||||
}
|
||||
|
||||
private static func findOrCreateSizingViewHeight(from bubbleData: MXKRoomBubbleCellData) -> SizingViewHeight {
|
||||
|
||||
let sizingViewHeight: SizingViewHeight
|
||||
let bubbleDataHashValue = bubbleData.hashValue
|
||||
|
||||
if let foundSizingViewHeight = self.Sizing.sizes.first(where: { (sizingViewHeight) -> Bool in
|
||||
return sizingViewHeight.uniqueIdentifier == bubbleDataHashValue
|
||||
}) {
|
||||
sizingViewHeight = foundSizingViewHeight
|
||||
} else {
|
||||
sizingViewHeight = SizingViewHeight(uniqueIdentifier: bubbleDataHashValue)
|
||||
}
|
||||
|
||||
return sizingViewHeight
|
||||
}
|
||||
|
||||
private static func contentViewHeight(for cellData: MXKCellData, fitting width: CGFloat) -> CGFloat {
|
||||
let sizingView = self.sizingView()
|
||||
|
||||
sizingView.render(cellData)
|
||||
sizingView.layoutIfNeeded()
|
||||
|
||||
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
|
||||
var height = sizingView.systemLayoutSizeFitting(fittingSize).height
|
||||
|
||||
if let roomBubbleCellData = cellData as? RoomBubbleCellData, let readReceipts = roomBubbleCellData.readReceipts, readReceipts.count > 0 {
|
||||
height+=RoomBubbleCellLayout.readReceiptsViewHeight
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BubbleCellReadReceiptsDisplayable
|
||||
extension KeyVerificationBaseBubbleCell: BubbleCellReadReceiptsDisplayable {
|
||||
|
||||
func addReadReceiptsView(_ readReceiptsView: UIView) {
|
||||
self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView)
|
||||
}
|
||||
|
||||
func removeReadReceiptsView() {
|
||||
self.bubbleCellContentView?.removeReadReceiptsView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class KeyVerificationCellInnerContentView: UIView, NibLoadable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let cornerRadius: CGFloat = 8.0
|
||||
static let buttonBackgroundColorAlpha: CGFloat = 0.2
|
||||
static let buttonCornerRadius: CGFloat = 6.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var badgeImageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var otherUserInformationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var requestStatusLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var buttonsContainerView: UIView!
|
||||
@IBOutlet private weak var acceptButton: UIButton!
|
||||
@IBOutlet private weak var declineButton: UIButton!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var isButtonsHidden: Bool {
|
||||
get {
|
||||
return self.acceptButton.isHidden && self.declineButton.isHidden
|
||||
}
|
||||
set {
|
||||
self.buttonsContainerView.isHidden = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isRequestStatusHidden: Bool {
|
||||
get {
|
||||
return self.requestStatusLabel.isHidden
|
||||
}
|
||||
set {
|
||||
self.requestStatusLabel.isHidden = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var badgeImage: UIImage? {
|
||||
get {
|
||||
return self.badgeImageView.image
|
||||
}
|
||||
set {
|
||||
self.badgeImageView.image = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var title: String? {
|
||||
get {
|
||||
return self.titleLabel.text
|
||||
}
|
||||
set {
|
||||
self.titleLabel.text = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var otherUserInfo: String? {
|
||||
return self.otherUserInformationLabel.text
|
||||
}
|
||||
|
||||
var requestStatusText: String? {
|
||||
get {
|
||||
return self.requestStatusLabel.text
|
||||
}
|
||||
set {
|
||||
self.requestStatusLabel.text = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var acceptActionHandler: (() -> Void)?
|
||||
|
||||
var declineActionHandler: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate() -> KeyVerificationCellInnerContentView {
|
||||
let view = KeyVerificationCellInnerContentView.loadFromNib()
|
||||
return view
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.layer.masksToBounds = true
|
||||
self.acceptButton.layer.masksToBounds = true
|
||||
|
||||
self.acceptButton.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
self.acceptButton.titleLabel?.minimumScaleFactor = 0.5
|
||||
self.acceptButton.titleLabel?.baselineAdjustment = .alignCenters
|
||||
|
||||
self.acceptButton.setTitle(VectorL10n.keyVerificationTileRequestIncomingApprovalAccept, for: .normal)
|
||||
|
||||
self.declineButton.layer.masksToBounds = true
|
||||
|
||||
self.declineButton.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
self.declineButton.titleLabel?.minimumScaleFactor = 0.5
|
||||
self.declineButton.titleLabel?.baselineAdjustment = .alignCenters
|
||||
|
||||
self.declineButton.setTitle(VectorL10n.keyVerificationTileRequestIncomingApprovalDecline, for: .normal)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.layer.cornerRadius = Constants.cornerRadius
|
||||
|
||||
if self.isButtonsHidden == false {
|
||||
self.acceptButton.layer.cornerRadius = Constants.buttonCornerRadius
|
||||
self.declineButton.layer.cornerRadius = Constants.buttonCornerRadius
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.backgroundColor = theme.headerBackgroundColor
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.otherUserInformationLabel.textColor = theme.textSecondaryColor
|
||||
|
||||
self.acceptButton.vc_setBackgroundColor(theme.tintColor.withAlphaComponent(Constants.buttonBackgroundColorAlpha), for: .normal)
|
||||
self.declineButton.vc_setBackgroundColor(theme.noticeColor.withAlphaComponent(Constants.buttonBackgroundColorAlpha), for: .normal)
|
||||
}
|
||||
|
||||
func updateSenderInfo(with userId: String, userDisplayName: String?) {
|
||||
self.otherUserInformationLabel.text = self.buildUserInfoText(with: userId, userDisplayName: userDisplayName)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildUserInfoText(with userId: String, userDisplayName: String?) -> String {
|
||||
|
||||
let userInfoText: String
|
||||
|
||||
if let userDisplayName = userDisplayName {
|
||||
userInfoText = "\(userId) (\(userDisplayName))"
|
||||
} else {
|
||||
userInfoText = userId
|
||||
}
|
||||
|
||||
return userInfoText
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
@IBAction private func declineButtonAction(_ sender: Any) {
|
||||
self.declineActionHandler?()
|
||||
}
|
||||
|
||||
@IBAction private func acceptButtonAction(_ sender: Any) {
|
||||
self.acceptActionHandler?()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?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" colorMatched="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<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"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KeyVerificationCellInnerContentView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="313" height="133"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="2Sr-GM-aAU">
|
||||
<rect key="frame" x="10" y="10" width="293" height="113"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="5gD-pX-GFz">
|
||||
<rect key="frame" x="70" y="0.0" width="153.5" height="16"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="encryption_normal" translatesAutoresizingMaskIntoConstraints="NO" id="EHJ-3L-OPJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="16" height="16"/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Verification request" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BOd-4B-LQX">
|
||||
<rect key="frame" x="20" y="0.0" width="133.5" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="751" text="User (@user:matrix.org)" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e1n-WP-GTb">
|
||||
<rect key="frame" x="83.5" y="26" width="126" height="13.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
<color key="textColor" red="0.1803921568627451" green="0.18431372549019609" blue="0.19607843137254902" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Waiting..." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e6I-aZ-RRO">
|
||||
<rect key="frame" x="118" y="49.5" width="57" height="13.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<color key="textColor" red="0.38039215686274508" green="0.4392156862745098" blue="0.54509803921568623" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Tr-gP-gPB">
|
||||
<rect key="frame" x="0.0" y="73" width="293" height="40"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="WxG-vh-Bn0">
|
||||
<rect key="frame" x="71" y="0.0" width="151" height="40"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="atD-LF-sGH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="72" height="40"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="Decline">
|
||||
<color key="titleColor" red="1" green="0.29411764705882354" blue="0.33333333333333331" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="declineButtonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="dS6-Xr-6jZ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="irs-8W-qcs">
|
||||
<rect key="frame" x="82" y="0.0" width="69" height="40"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="Accept">
|
||||
<color key="titleColor" red="0.011764705882352941" green="0.70196078431372544" blue="0.50588235294117645" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="acceptButtonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="IQ6-be-vJt"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="atD-LF-sGH" firstAttribute="height" secondItem="WxG-vh-Bn0" secondAttribute="height" priority="999" id="3sT-HV-Os0"/>
|
||||
<constraint firstItem="irs-8W-qcs" firstAttribute="height" secondItem="WxG-vh-Bn0" secondAttribute="height" priority="999" id="CfM-Vc-MQ2"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="WxG-vh-Bn0" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="5Tr-gP-gPB" secondAttribute="leading" id="4al-oB-NpF"/>
|
||||
<constraint firstAttribute="bottom" secondItem="WxG-vh-Bn0" secondAttribute="bottom" id="rCo-gP-UF5"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="WxG-vh-Bn0" secondAttribute="trailing" id="u47-KK-aKk"/>
|
||||
<constraint firstAttribute="height" priority="999" constant="40" id="ujN-jh-fde"/>
|
||||
<constraint firstItem="WxG-vh-Bn0" firstAttribute="top" secondItem="5Tr-gP-gPB" secondAttribute="top" id="wNH-CW-iLB"/>
|
||||
<constraint firstItem="WxG-vh-Bn0" firstAttribute="centerX" secondItem="5Tr-gP-gPB" secondAttribute="centerX" id="yWq-qo-MY7"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="5Tr-gP-gPB" firstAttribute="width" secondItem="2Sr-GM-aAU" secondAttribute="width" id="VJx-xp-NZE"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.95294117647058818" green="0.97254901960784312" blue="0.99215686274509807" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="2Sr-GM-aAU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="M7S-MY-Jxk"/>
|
||||
<constraint firstItem="2Sr-GM-aAU" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="SfH-5W-p30"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2Sr-GM-aAU" secondAttribute="trailing" constant="10" id="rVw-1F-ch3"/>
|
||||
<constraint firstAttribute="bottom" secondItem="2Sr-GM-aAU" secondAttribute="bottom" constant="10" id="vL2-dm-qbe"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="acceptButton" destination="irs-8W-qcs" id="LKq-qE-bbg"/>
|
||||
<outlet property="badgeImageView" destination="EHJ-3L-OPJ" id="eNW-bg-eYy"/>
|
||||
<outlet property="buttonsContainerView" destination="5Tr-gP-gPB" id="aqy-vT-mu3"/>
|
||||
<outlet property="declineButton" destination="atD-LF-sGH" id="CfI-FW-ySy"/>
|
||||
<outlet property="otherUserInformationLabel" destination="e1n-WP-GTb" id="jhY-gH-QnO"/>
|
||||
<outlet property="requestStatusLabel" destination="e6I-aZ-RRO" id="zQf-qy-3Rq"/>
|
||||
<outlet property="titleLabel" destination="BOd-4B-LQX" id="4sw-3J-faF"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-828.26086956521749" y="-386.04910714285711"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="encryption_normal" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
class KeyVerificationConclusionBubbleCell: KeyVerificationBaseBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationConclusionBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.keyVerificationCellInnerContentView?.isButtonsHidden = true
|
||||
self.keyVerificationCellInnerContentView?.isRequestStatusHidden = true
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override func render(_ cellData: MXKCellData!) {
|
||||
super.render(cellData)
|
||||
|
||||
guard let keyVerificationCellInnerContentView = self.keyVerificationCellInnerContentView,
|
||||
let bubbleData = self.bubbleData as? RoomBubbleCellData,
|
||||
let viewData = self.viewData(from: bubbleData) else {
|
||||
NSLog("[KeyVerificationConclusionBubbleCell] Fail to render \(String(describing: cellData))")
|
||||
return
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.badgeImage = viewData.badgeImage
|
||||
keyVerificationCellInnerContentView.title = viewData.title
|
||||
keyVerificationCellInnerContentView.updateSenderInfo(with: viewData.senderId, userDisplayName: viewData.senderDisplayName)
|
||||
}
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func viewData(from roomBubbleData: RoomBubbleCellData) -> KeyVerificationConclusionViewData? {
|
||||
guard let event = roomBubbleData.bubbleComponents.first?.event else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let viewData: KeyVerificationConclusionViewData?
|
||||
|
||||
let senderId = self.senderId(from: bubbleData)
|
||||
let senderDisplayName = self.senderDisplayName(from: bubbleData)
|
||||
let title: String?
|
||||
let badgeImage: UIImage?
|
||||
|
||||
switch event.eventType {
|
||||
case .keyVerificationDone:
|
||||
badgeImage = Asset.Images.encryptionTrusted.image
|
||||
title = VectorL10n.keyVerificationTileConclusionDoneTitle
|
||||
case .keyVerificationCancel:
|
||||
badgeImage = Asset.Images.encryptionWarning.image
|
||||
title = VectorL10n.keyVerificationTileConclusionWarningTitle
|
||||
default:
|
||||
badgeImage = nil
|
||||
title = nil
|
||||
}
|
||||
|
||||
if let title = title, let badgeImage = badgeImage {
|
||||
viewData = KeyVerificationConclusionViewData(badgeImage: badgeImage,
|
||||
title: title,
|
||||
senderId: senderId,
|
||||
senderDisplayName: senderDisplayName)
|
||||
} else {
|
||||
viewData = nil
|
||||
}
|
||||
|
||||
return viewData
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct KeyVerificationConclusionViewData {
|
||||
let badgeImage: UIImage
|
||||
let title: String
|
||||
let senderId: String
|
||||
let senderDisplayName: String?
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
final class KeyVerificationConclusionWithPaginationTitleBubbleCell: KeyVerificationConclusionBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationConclusionWithPaginationTitleBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
guard let bubbleCellContentView = self.bubbleCellContentView else {
|
||||
fatalError("[KeyVerificationConclusionWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil")
|
||||
}
|
||||
|
||||
bubbleCellContentView.showPaginationTitle = true
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
class KeyVerificationIncomingRequestApprovalBubbleCell: KeyVerificationBaseBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationIncomingRequestApprovalBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
guard let keyVerificationCellInnerContentView = self.keyVerificationCellInnerContentView else {
|
||||
fatalError("[KeyVerificationIncomingRequestApprovalBubbleCell] keyVerificationCellInnerContentView should not be nil")
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.isButtonsHidden = false
|
||||
keyVerificationCellInnerContentView.isRequestStatusHidden = true
|
||||
keyVerificationCellInnerContentView.badgeImage = Asset.Images.encryptionNormal.image
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
|
||||
self.keyVerificationCellInnerContentView?.acceptActionHandler = nil
|
||||
self.keyVerificationCellInnerContentView?.declineActionHandler = nil
|
||||
}
|
||||
|
||||
override func render(_ cellData: MXKCellData!) {
|
||||
super.render(cellData)
|
||||
|
||||
guard let keyVerificationCellInnerContentView = self.keyVerificationCellInnerContentView,
|
||||
let bubbleData = self.bubbleData,
|
||||
let viewData = self.viewData(from: bubbleData) else {
|
||||
NSLog("[KeyVerificationIncomingRequestApprovalBubbleCell] Fail to render \(String(describing: cellData))")
|
||||
return
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.title = viewData.title
|
||||
keyVerificationCellInnerContentView.updateSenderInfo(with: viewData.senderId, userDisplayName: viewData.senderDisplayName)
|
||||
|
||||
let actionUserInfo: [AnyHashable: Any]?
|
||||
|
||||
if let eventId = bubbleData.getFirstBubbleComponentWithDisplay()?.event.eventId {
|
||||
actionUserInfo = [kMXKRoomBubbleCellEventIdKey: eventId]
|
||||
} else {
|
||||
actionUserInfo = nil
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.acceptActionHandler = { [weak self] in
|
||||
self?.delegate?.cell(self, didRecognizeAction: kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed, userInfo: actionUserInfo)
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.declineActionHandler = { [weak self] in
|
||||
self?.delegate?.cell(self, didRecognizeAction: kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed, userInfo: actionUserInfo)
|
||||
}
|
||||
}
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func viewData(from bubbleData: MXKRoomBubbleCellData) -> KeyVerificationIncomingRequestApprovalViewData? {
|
||||
|
||||
let senderId = self.senderId(from: bubbleData)
|
||||
let senderDisplayName = self.senderDisplayName(from: bubbleData)
|
||||
let title = VectorL10n.keyVerificationTileRequestIncomingTitle
|
||||
|
||||
return KeyVerificationIncomingRequestApprovalViewData(title: title,
|
||||
senderId: senderId,
|
||||
senderDisplayName: senderDisplayName)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct KeyVerificationIncomingRequestApprovalViewData {
|
||||
let title: String
|
||||
let senderId: String
|
||||
let senderDisplayName: String?
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
final class KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell: KeyVerificationIncomingRequestApprovalBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
guard let bubbleCellContentView = self.bubbleCellContentView else {
|
||||
fatalError("[KeyVerificationRequestStatusWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil")
|
||||
}
|
||||
|
||||
bubbleCellContentView.showPaginationTitle = true
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
class KeyVerificationRequestStatusBubbleCell: KeyVerificationBaseBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationRequestStatusBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
guard let keyVerificationCellInnerContentView = self.keyVerificationCellInnerContentView else {
|
||||
fatalError("[KeyVerificationRequestStatusBubbleCell] keyVerificationCellInnerContentView should not be nil")
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.isButtonsHidden = true
|
||||
keyVerificationCellInnerContentView.isRequestStatusHidden = false
|
||||
keyVerificationCellInnerContentView.badgeImage = Asset.Images.encryptionNormal.image
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override func render(_ cellData: MXKCellData!) {
|
||||
super.render(cellData)
|
||||
|
||||
guard let keyVerificationCellInnerContentView = self.keyVerificationCellInnerContentView,
|
||||
let roomBubbleCellData = self.bubbleData as? RoomBubbleCellData,
|
||||
let viewData = self.viewData(from: roomBubbleCellData) else {
|
||||
NSLog("[KeyVerificationRequestStatusBubbleCell] Fail to render \(String(describing: cellData))")
|
||||
return
|
||||
}
|
||||
|
||||
keyVerificationCellInnerContentView.title = viewData.title
|
||||
keyVerificationCellInnerContentView.updateSenderInfo(with: viewData.senderId, userDisplayName: viewData.senderDisplayName)
|
||||
keyVerificationCellInnerContentView.requestStatusText = viewData.statusText
|
||||
}
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func viewData(from roomBubbleCellData: RoomBubbleCellData) -> KeyVerificationRequestStatusViewData? {
|
||||
|
||||
let senderId = self.senderId(from: bubbleData)
|
||||
let senderDisplayName = self.senderDisplayName(from: bubbleData)
|
||||
let title: String
|
||||
let statusText: String?
|
||||
|
||||
if roomBubbleCellData.isIncoming {
|
||||
title = VectorL10n.keyVerificationTileRequestIncomingTitle
|
||||
} else {
|
||||
title = VectorL10n.keyVerificationTileRequestOutgoingTitle
|
||||
}
|
||||
|
||||
if let keyVerification = roomBubbleCellData.keyVerification {
|
||||
switch keyVerification.state {
|
||||
case .requestPending:
|
||||
if !roomBubbleCellData.isIncoming {
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusWaiting
|
||||
} else {
|
||||
if roomBubbleCellData.isKeyVerificationOperationPending {
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusDataLoading
|
||||
} else {
|
||||
// Should not happen, KeyVerificationIncomingRequestApprovalBubbleCell should be displayed in this case.
|
||||
statusText = nil
|
||||
}
|
||||
}
|
||||
case .requestExpired:
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusExpired
|
||||
case .requestCancelled, .transactionCancelled:
|
||||
let userName = senderDisplayName ?? senderId
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusCancelled(userName)
|
||||
case .requestCancelledByMe, .transactionCancelledByMe:
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusCancelledByMe
|
||||
default:
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusAccepted
|
||||
}
|
||||
} else {
|
||||
statusText = VectorL10n.keyVerificationTileRequestStatusDataLoading
|
||||
}
|
||||
|
||||
let viewData: KeyVerificationRequestStatusViewData?
|
||||
|
||||
if let statusText = statusText {
|
||||
viewData = KeyVerificationRequestStatusViewData(title: title,
|
||||
senderId: senderId,
|
||||
senderDisplayName: senderDisplayName,
|
||||
statusText: statusText)
|
||||
} else {
|
||||
viewData = nil
|
||||
}
|
||||
|
||||
return viewData
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct KeyVerificationRequestStatusViewData {
|
||||
let title: String
|
||||
let senderId: String
|
||||
let senderDisplayName: String?
|
||||
let statusText: String
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
final class KeyVerificationRequestStatusWithPaginationTitleBubbleCell: KeyVerificationRequestStatusBubbleCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static let view = KeyVerificationRequestStatusWithPaginationTitleBubbleCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
guard let bubbleCellContentView = self.bubbleCellContentView else {
|
||||
fatalError("[KeyVerificationRequestStatusWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil")
|
||||
}
|
||||
|
||||
bubbleCellContentView.showPaginationTitle = true
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override class func sizingView() -> KeyVerificationBaseBubbleCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
final class SizingViewHeight: Hashable, Equatable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let uniqueIdentifier: Int
|
||||
var heights: [CGFloat /* width */: CGFloat /* height */] = [:]
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(uniqueIdentifier: Int) {
|
||||
self.uniqueIdentifier = uniqueIdentifier
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(self.uniqueIdentifier)
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
static func == (lhs: SizingViewHeight, rhs: SizingViewHeight) -> Bool {
|
||||
return lhs.uniqueIdentifier == rhs.uniqueIdentifier
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@ typedef enum : NSUInteger
|
|||
`RoomInputToolbarView` instance is a view used to handle all kinds of available inputs
|
||||
for a room (message composer, attachments selection...).
|
||||
*/
|
||||
@interface RoomInputToolbarView : MXKRoomInputToolbarViewWithHPGrowingText <MediaPickerViewControllerDelegate>
|
||||
@interface RoomInputToolbarView : MXKRoomInputToolbarViewWithHPGrowingText
|
||||
|
||||
/**
|
||||
The delegate notified when inputs are ready.
|
||||
|
|
|
@ -128,8 +128,6 @@
|
|||
|
||||
if (_isEncryptionEnabled)
|
||||
{
|
||||
self.encryptedRoomIcon.image = [UIImage imageNamed:@"e2e_verified"];
|
||||
|
||||
// Check the device screen size before using large placeholder
|
||||
if ([GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay4p7Inch)
|
||||
{
|
||||
|
@ -138,8 +136,6 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
self.encryptedRoomIcon.image = [UIImage imageNamed:@"e2e_unencrypted"];
|
||||
|
||||
// Check the device screen size before using large placeholder
|
||||
if ([GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay4p7Inch)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
@interface ExpandedRoomTitleView : RoomTitleView
|
||||
|
||||
@property (weak, nonatomic) IBOutlet MXKImageView *roomAvatar;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *roomAvatarBadgeImageView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomAvatarHeaderBackground;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" 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="14460.20"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -27,11 +27,20 @@
|
|||
<constraint firstAttribute="width" constant="84" id="Yye-G1-hCH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="14n-e5-ix3">
|
||||
<rect key="frame" x="318" y="71" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="14n-e5-ix3" secondAttribute="height" multiplier="1:1" id="g2h-P2-8gQ"/>
|
||||
<constraint firstAttribute="width" constant="24" id="oSg-O6-MRx"/>
|
||||
</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="RoomAvatarHeaderBackground"/>
|
||||
<constraints>
|
||||
<constraint firstItem="14n-e5-ix3" firstAttribute="trailing" secondItem="eEk-n9-4dA" secondAttribute="trailing" id="3fH-JR-GgP"/>
|
||||
<constraint firstAttribute="height" constant="95" id="DzD-aR-3eV"/>
|
||||
<constraint firstItem="14n-e5-ix3" firstAttribute="bottom" secondItem="eEk-n9-4dA" secondAttribute="bottom" id="Or6-Ka-Yny"/>
|
||||
<constraint firstItem="eEk-n9-4dA" firstAttribute="centerX" secondItem="uSp-YH-L18" secondAttribute="centerX" id="W1w-ZX-f2d"/>
|
||||
<constraint firstAttribute="bottom" secondItem="eEk-n9-4dA" secondAttribute="bottom" id="hXd-bM-Bbb"/>
|
||||
</constraints>
|
||||
|
@ -148,6 +157,7 @@
|
|||
<outlet property="displayNameTextField" destination="6uH-I3-RQg" id="MfX-LQ-C2K"/>
|
||||
<outlet property="membersListIcon" destination="S3Y-wJ-HOe" id="hkf-a4-Ffw"/>
|
||||
<outlet property="roomAvatar" destination="eEk-n9-4dA" id="5jd-Dx-tXE"/>
|
||||
<outlet property="roomAvatarBadgeImageView" destination="14n-e5-ix3" id="ZSS-wz-Oi7"/>
|
||||
<outlet property="roomAvatarHeaderBackground" destination="uSp-YH-L18" id="sfW-ED-5bD"/>
|
||||
<outlet property="roomAvatarHeaderBackgroundHeightConstraint" destination="DzD-aR-3eV" id="SuC-mO-epX"/>
|
||||
<outlet property="roomDetailsMask" destination="MFb-0F-eO8" id="ajK-sr-qf7"/>
|
||||
|
@ -155,10 +165,11 @@
|
|||
<outlet property="roomTopic" destination="qD3-kA-DSI" id="mOj-AU-7LM"/>
|
||||
<outlet property="titleMask" destination="8HH-9b-1yH" id="MFh-3r-I5e"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="129.59999999999999" y="153.37331334332833"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="add_participant" width="92.800003051757812" height="92.800003051757812"/>
|
||||
<image name="members_list_icon" width="15" height="15"/>
|
||||
<image name="add_participant" width="30" height="30"/>
|
||||
<image name="members_list_icon" width="15" height="12"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -151,10 +151,10 @@
|
|||
deviceVerificationCoordinatorBridgePresenter = nil;
|
||||
|
||||
// Check device new status
|
||||
[self.mxSession.crypto downloadKeys:@[self.device.userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap) {
|
||||
[self.mxSession.crypto downloadKeys:@[self.device.userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:self.device.deviceId forUser:self.device.userId];
|
||||
if (deviceInfo && deviceInfo.verified == MXDeviceVerified)
|
||||
if (deviceInfo && deviceInfo.trustLevel.localVerificationStatus == MXDeviceVerified)
|
||||
{
|
||||
// Accept the received requests from this device
|
||||
// As the device is now verified, all other key requests will be automatically accepted.
|
||||
|
|
|
@ -248,13 +248,17 @@ private enum BackupRows {
|
|||
|
||||
if device.fingerprint == self.userDevice.fingerprint {
|
||||
return VectorL10n.settingsKeyBackupInfoTrustSignatureValid
|
||||
} else if signature.valid && (device.verified == MXDeviceVerified) {
|
||||
} else if signature.valid
|
||||
&& (device.trustLevel.localVerificationStatus == .verified) {
|
||||
return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceVerified(displayName)
|
||||
} else if signature.valid && (device.verified != MXDeviceVerified) {
|
||||
} else if signature.valid
|
||||
&& (device.trustLevel.localVerificationStatus != .verified) {
|
||||
return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(displayName)
|
||||
} else if !signature.valid && (device.verified == MXDeviceVerified) {
|
||||
} else if !signature.valid
|
||||
&& (device.trustLevel.localVerificationStatus == .verified) {
|
||||
return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(displayName)
|
||||
} else if !signature.valid && (device.verified != MXDeviceVerified) {
|
||||
} else if !signature.valid
|
||||
&& (device.trustLevel.localVerificationStatus != .verified) {
|
||||
return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(displayName)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?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="QHp-lE-M7m">
|
||||
<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="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Manage Session View Controller-->
|
||||
<scene sceneID="KQ6-RY-1vS">
|
||||
<objects>
|
||||
<tableViewController id="QHp-lE-M7m" customClass="ManageSessionViewController" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Fpr-At-4Wb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="JHA-Ac-OAZ">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JHA-Ac-OAZ" id="Soh-58-aeC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="QHp-lE-M7m" id="F5e-nl-xhe"/>
|
||||
<outlet property="delegate" destination="QHp-lE-M7m" id="TJn-MA-abi"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Zo3-gk-o9e" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="65" y="68"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
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 <MatrixKit/MatrixKit.h>
|
||||
|
||||
|
||||
@interface ManageSessionViewController : MXKTableViewController
|
||||
|
||||
+ (ManageSessionViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession andDevice:(MXDevice*)device;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
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 "ManageSessionViewController.h"
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import <OLMKit/OLMKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "AvatarGenerator.h"
|
||||
|
||||
#import "ThemeService.h"
|
||||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SECTION_SESSION_INFO,
|
||||
SECTION_ACTION,
|
||||
SECTION_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
SESSION_INFO_SESSION_NAME,
|
||||
SESSION_INFO_TRUST,
|
||||
SESSION_INFO_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTION_REMOVE_SESSION,
|
||||
ACTION_COUNT
|
||||
};
|
||||
|
||||
|
||||
@interface ManageSessionViewController ()
|
||||
{
|
||||
// The device to display
|
||||
MXDevice *device;
|
||||
|
||||
// Current alert (if any).
|
||||
UIAlertController *currentAlert;
|
||||
|
||||
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
||||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
|
||||
// The current pushed view controller
|
||||
UIViewController *pushedViewController;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ManageSessionViewController
|
||||
|
||||
#pragma mark - Setup & Teardown
|
||||
|
||||
+ (ManageSessionViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession andDevice:(MXDevice*)device;
|
||||
{
|
||||
ManageSessionViewController* viewController = [[UIStoryboard storyboardWithName:@"ManageSession" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
|
||||
[viewController addMatrixSession:matrixSession];
|
||||
viewController->device = device;
|
||||
return viewController;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - View life cycle
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
|
||||
self.navigationItem.title = NSLocalizedStringFromTable(@"manage_session_title", @"Vector", nil);
|
||||
|
||||
// Remove back bar button title when pushing a view controller
|
||||
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
||||
|
||||
[self.tableView registerClass:MXKTableViewCellWithLabelAndTextField.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndTextField defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
|
||||
[self.tableView registerNib:MXKTableViewCellWithTextView.nib forCellReuseIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
|
||||
|
||||
// Enable self sizing cells
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 50;
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
}];
|
||||
[self userInterfaceThemeDidChange];
|
||||
}
|
||||
|
||||
- (void)userInterfaceThemeDidChange
|
||||
{
|
||||
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
||||
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
// Check the table view style to select its bg color.
|
||||
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
|
||||
self.view.backgroundColor = self.tableView.backgroundColor;
|
||||
self.tableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return ThemeService.shared.theme.statusBarStyle;
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
if (kThemeServiceDidChangeThemeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
|
||||
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"ManageSession"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
// Refresh display
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
currentAlert = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)pushViewController:(UIViewController*)viewController
|
||||
{
|
||||
// Keep ref on pushed view controller
|
||||
pushedViewController = viewController;
|
||||
|
||||
// Hide back button title
|
||||
self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
||||
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
|
||||
- (void)releasePushedViewController
|
||||
{
|
||||
if (pushedViewController)
|
||||
{
|
||||
if ([pushedViewController isKindOfClass:[UINavigationController class]])
|
||||
{
|
||||
UINavigationController *navigationController = (UINavigationController*)pushedViewController;
|
||||
for (id subViewController in navigationController.viewControllers)
|
||||
{
|
||||
if ([subViewController respondsToSelector:@selector(destroy)])
|
||||
{
|
||||
[subViewController destroy];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([pushedViewController respondsToSelector:@selector(destroy)])
|
||||
{
|
||||
[(id)pushedViewController destroy];
|
||||
}
|
||||
|
||||
pushedViewController = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
// Remove observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
// Trigger a full table reloadData
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadDeviceWithCompletion:(void (^)(void))completion
|
||||
{
|
||||
MXWeakify(self);
|
||||
[self.mainSession.matrixRestClient deviceByDeviceId:device.deviceId success:^(MXDevice *device) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
self->device = device;
|
||||
[self reloadData];
|
||||
completion();
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[ManageSessionVC] reloadDeviceWithCompletion failed. Error: %@", error);
|
||||
[self reloadData];
|
||||
completion();
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Segues
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
// Keep ref on destinationViewController
|
||||
[super prepareForSegue:segue sender:sender];
|
||||
|
||||
// FIXME add night mode
|
||||
}
|
||||
|
||||
#pragma mark - UITableView data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return SECTION_COUNT;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_SESSION_INFO:
|
||||
count = SESSION_INFO_COUNT;
|
||||
break;
|
||||
case SECTION_ACTION:
|
||||
count = ACTION_COUNT;
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithLabelAndTextField*)getLabelAndTextFieldCell:(UITableView*)tableview forIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKTableViewCellWithLabelAndTextField *cell = [tableview dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndTextField defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left;
|
||||
cell.mxkTextFieldLeadingConstraint.constant = 16;
|
||||
cell.mxkTextFieldTrailingConstraint.constant = 15;
|
||||
|
||||
cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
cell.mxkTextField.userInteractionEnabled = YES;
|
||||
cell.mxkTextField.borderStyle = UITextBorderStyleNone;
|
||||
cell.mxkTextField.textAlignment = NSTextAlignmentRight;
|
||||
cell.mxkTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
cell.mxkTextField.font = [UIFont systemFontOfSize:16];
|
||||
cell.mxkTextField.placeholder = nil;
|
||||
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
cell.accessoryView = nil;
|
||||
|
||||
cell.alpha = 1.0f;
|
||||
cell.userInteractionEnabled = YES;
|
||||
|
||||
[cell layoutIfNeeded];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithLabelAndSwitch*)getLabelAndSwitchCell:(UITableView*)tableview forIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch *cell = [tableview dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left;
|
||||
cell.mxkSwitchTrailingConstraint.constant = 15;
|
||||
|
||||
cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
[cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// Force layout before reusing a cell (fix switch displayed outside the screen)
|
||||
[cell layoutIfNeeded];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCell*)getDefaultTableViewCell:(UITableView*)tableView
|
||||
{
|
||||
MXKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
|
||||
if (!cell)
|
||||
{
|
||||
cell = [[MXKTableViewCell alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
cell.accessoryView = nil;
|
||||
cell.imageView.image = nil;
|
||||
}
|
||||
cell.textLabel.accessibilityIdentifier = nil;
|
||||
cell.textLabel.font = [UIFont systemFontOfSize:17];
|
||||
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
cell.contentView.backgroundColor = UIColor.clearColor;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCell*)trustCellWithDevice:(MXDevice*)device forTableView:(UITableView*)tableView
|
||||
{
|
||||
MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView];
|
||||
|
||||
NSString *deviceId = device.deviceId;
|
||||
MXDeviceInfo *deviceInfo = [self.mainSession.crypto deviceWithDeviceId:deviceId ofUser:self.mainSession.myUser.userId];
|
||||
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
if (deviceInfo.trustLevel.isVerified)
|
||||
{
|
||||
cell.textLabel.text = NSLocalizedStringFromTable(@"manage_session_trusted", @"Vector", nil);
|
||||
cell.imageView.image = [UIImage imageNamed:@"encryption_trusted"];
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel.text = NSLocalizedStringFromTable(@"manage_session_not_trusted", @"Vector", nil);
|
||||
cell.imageView.image = [UIImage imageNamed:@"encryption_warning"];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCell*)descriptionCellForTableView:(UITableView*)tableView withText:(NSString*)text
|
||||
{
|
||||
MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView];
|
||||
cell.textLabel.text = text;
|
||||
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
- (MXKTableViewCellWithTextView*)textViewCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKTableViewCellWithTextView *textViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
textViewCell.mxkTextView.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
textViewCell.mxkTextView.font = [UIFont systemFontOfSize:17];
|
||||
textViewCell.mxkTextView.backgroundColor = [UIColor clearColor];
|
||||
textViewCell.mxkTextViewLeadingConstraint.constant = tableView.separatorInset.left;
|
||||
textViewCell.mxkTextViewTrailingConstraint.constant = tableView.separatorInset.right;
|
||||
textViewCell.mxkTextView.accessibilityIdentifier = nil;
|
||||
|
||||
return textViewCell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
// set the cell to a default value to avoid application crashes
|
||||
UITableViewCell *cell = [[UITableViewCell alloc] init];
|
||||
cell.backgroundColor = [UIColor redColor];
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_SESSION_INFO:
|
||||
switch (row)
|
||||
{
|
||||
case SESSION_INFO_SESSION_NAME:
|
||||
{
|
||||
MXKTableViewCellWithLabelAndTextField *displaynameCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
|
||||
|
||||
displaynameCell.mxkLabel.text = NSLocalizedStringFromTable(@"manage_session_name", @"Vector", nil);
|
||||
displaynameCell.mxkTextField.text = device.displayName;
|
||||
displaynameCell.mxkTextField.userInteractionEnabled = NO;
|
||||
displaynameCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
|
||||
cell = displaynameCell;
|
||||
break;
|
||||
}
|
||||
case SESSION_INFO_TRUST:
|
||||
{
|
||||
cell = [self trustCellWithDevice:device forTableView:tableView];
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case SECTION_ACTION:
|
||||
switch (row)
|
||||
{
|
||||
case ACTION_REMOVE_SESSION:
|
||||
{
|
||||
MXKTableViewCellWithButton *removeSessionBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
|
||||
if (!removeSessionBtnCell)
|
||||
{
|
||||
removeSessionBtnCell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix https://github.com/vector-im/riot-ios/issues/1354
|
||||
removeSessionBtnCell.mxkButton.titleLabel.text = nil;
|
||||
}
|
||||
|
||||
NSString *btnTitle = NSLocalizedStringFromTable(@"manage_session_sign_out", @"Vector", nil);
|
||||
[removeSessionBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
|
||||
[removeSessionBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
|
||||
[removeSessionBtnCell.mxkButton setTintColor:ThemeService.shared.theme.warningColor];
|
||||
removeSessionBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
removeSessionBtnCell.mxkButton.userInteractionEnabled = NO;
|
||||
removeSessionBtnCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
|
||||
cell = removeSessionBtnCell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_SESSION_INFO:
|
||||
return NSLocalizedStringFromTable(@"manage_session_info", @"Vector", nil);
|
||||
case SECTION_ACTION:
|
||||
return @"";
|
||||
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
if ([view isKindOfClass:UITableViewHeaderFooterView.class])
|
||||
{
|
||||
// Customize label style
|
||||
UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view;
|
||||
tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
tableViewHeaderFooterView.textLabel.font = [UIFont systemFontOfSize:15];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UITableView delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
{
|
||||
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone)
|
||||
{
|
||||
// Update the selected background view
|
||||
if (ThemeService.shared.theme.selectedBackgroundColor)
|
||||
{
|
||||
cell.selectedBackgroundView = [[UIView alloc] init];
|
||||
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tableView.style == UITableViewStylePlain)
|
||||
{
|
||||
cell.selectedBackgroundView = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.selectedBackgroundView.backgroundColor = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == SECTION_SESSION_INFO)
|
||||
{
|
||||
return 44;
|
||||
}
|
||||
return 24;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
|
||||
{
|
||||
if (section == SECTION_SESSION_INFO)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 24;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.tableView == tableView)
|
||||
{
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_SESSION_INFO:
|
||||
switch (row)
|
||||
{
|
||||
case SESSION_INFO_SESSION_NAME:
|
||||
[self renameDevice];
|
||||
break;
|
||||
case SESSION_INFO_TRUST:
|
||||
[self showTrustForDevice:device];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SECTION_ACTION:
|
||||
{
|
||||
switch (row)
|
||||
{
|
||||
case ACTION_REMOVE_SESSION:
|
||||
[self removeDevice];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - actions
|
||||
|
||||
- (void)renameDevice
|
||||
{
|
||||
// Prompt the user to enter a device name.
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
MXWeakify(self);
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"device_details_rename_prompt_title"]
|
||||
message:[NSBundle mxk_localizedStringForKey:@"device_details_rename_prompt_message"] preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
textField.secureTextEntry = NO;
|
||||
textField.placeholder = nil;
|
||||
textField.keyboardType = UIKeyboardTypeDefault;
|
||||
textField.text = self->device.displayName;
|
||||
}];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
NSString *text = [self->currentAlert textFields].firstObject.text;
|
||||
self->currentAlert = nil;
|
||||
|
||||
|
||||
// Hot change
|
||||
self->device.displayName = text;
|
||||
[self reloadData];
|
||||
[self.activityIndicator startAnimating];
|
||||
|
||||
[self.mainSession.matrixRestClient setDeviceName:text forDeviceId:self->device.deviceId success:^{
|
||||
[self reloadDeviceWithCompletion:^{
|
||||
[self.activityIndicator stopAnimating];
|
||||
}];
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ManageSessionVC] Rename device (%@) failed", self->device.deviceId);
|
||||
[self reloadDeviceWithCompletion:^{
|
||||
[self.activityIndicator stopAnimating];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}];
|
||||
|
||||
}]];
|
||||
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showTrustForDevice:(MXDevice *)device
|
||||
{
|
||||
UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter = [[UserVerificationCoordinatorBridgePresenter alloc] initWithPresenter:self
|
||||
session:self.mainSession
|
||||
userId:self.mainSession.myUser.userId
|
||||
userDisplayName:nil
|
||||
deviceId:device.deviceId];
|
||||
[userVerificationCoordinatorBridgePresenter start];
|
||||
self.userVerificationCoordinatorBridgePresenter = userVerificationCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
- (void)removeDevice
|
||||
{
|
||||
// Get an authentication session to prepare device deletion
|
||||
[self.activityIndicator startAnimating];
|
||||
|
||||
MXWeakify(self);
|
||||
[self.mainSession.matrixRestClient getSessionToDeleteDeviceByDeviceId:device.deviceId success:^(MXAuthenticationSession *authSession) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// Check whether the password based type is supported
|
||||
BOOL isPasswordBasedTypeSupported = NO;
|
||||
for (MXLoginFlow *loginFlow in authSession.flows)
|
||||
{
|
||||
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword] || [loginFlow.stages indexOfObject:kMXLoginFlowTypePassword] != NSNotFound)
|
||||
{
|
||||
isPasswordBasedTypeSupported = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPasswordBasedTypeSupported && authSession.session)
|
||||
{
|
||||
// Prompt for a password
|
||||
[self->currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
// Prompt the user before deleting the device.
|
||||
self->currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"device_details_delete_prompt_title"] message:[NSBundle mxk_localizedStringForKey:@"device_details_delete_prompt_message"] preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
|
||||
[self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
|
||||
textField.secureTextEntry = YES;
|
||||
textField.placeholder = nil;
|
||||
textField.keyboardType = UIKeyboardTypeDefault;
|
||||
}];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
self->currentAlert = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
}]];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"submit"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
|
||||
UITextField *textField = [self->currentAlert textFields].firstObject;
|
||||
self->currentAlert = nil;
|
||||
|
||||
NSString *userId = self.mainSession.myUser.userId;
|
||||
NSDictionary *authParams;
|
||||
|
||||
// Sanity check
|
||||
if (userId)
|
||||
{
|
||||
authParams = @{@"session":authSession.session,
|
||||
@"user": userId,
|
||||
@"password": textField.text,
|
||||
@"type": kMXLoginFlowTypePassword};
|
||||
|
||||
}
|
||||
|
||||
[self.mainSession.matrixRestClient deleteDeviceByDeviceId:self->device.deviceId authParams:authParams success:^{
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
// We cannot stay in this screen anymore
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[ManageSessionVC] Delete device (%@) failed", self->device.deviceId);
|
||||
[self.activityIndicator stopAnimating];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}]];
|
||||
|
||||
[self presentViewController:self->currentAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[ManageSessionVC] Delete device (%@) failed, auth session flow type is not supported", self->device.deviceId);
|
||||
[self.activityIndicator stopAnimating];
|
||||
//[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[ManageSessionVC] Delete device (%@) failed, unable to get auth session", self->device.deviceId);
|
||||
[self.activityIndicator stopAnimating];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
41
Riot/Modules/Settings/Security/Security.storyboard
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?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="QHp-lE-M7m">
|
||||
<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="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Security View Controller-->
|
||||
<scene sceneID="KQ6-RY-1vS">
|
||||
<objects>
|
||||
<tableViewController id="QHp-lE-M7m" customClass="SecurityViewController" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Fpr-At-4Wb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="JHA-Ac-OAZ">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JHA-Ac-OAZ" id="Soh-58-aeC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="QHp-lE-M7m" id="F5e-nl-xhe"/>
|
||||
<outlet property="delegate" destination="QHp-lE-M7m" id="TJn-MA-abi"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Zo3-gk-o9e" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="65" y="68"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
24
Riot/Modules/Settings/Security/SecurityViewController.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
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 <MatrixKit/MatrixKit.h>
|
||||
|
||||
@interface SecurityViewController : MXKTableViewController
|
||||
|
||||
+ (SecurityViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession;
|
||||
|
||||
@end
|
||||
|
994
Riot/Modules/Settings/Security/SecurityViewController.m
Normal file
|
@ -0,0 +1,994 @@
|
|||
/*
|
||||
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 "SecurityViewController.h"
|
||||
|
||||
#import "ManageSessionViewController.h"
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import <OLMKit/OLMKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "AvatarGenerator.h"
|
||||
|
||||
#import "ThemeService.h"
|
||||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SECTION_CRYPTO_SESSIONS,
|
||||
SECTION_KEYBACKUP,
|
||||
SECTION_ADVANCED,
|
||||
SECTION_DEBUG, // TODO: To remove
|
||||
SECTION_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
ADVANCED_BLACKLIST_UNVERIFIED_DEVICES,
|
||||
ADVANCED_BLACKLIST_UNVERIFIED_DEVICES_DESCRIPTION,
|
||||
ADVANCED_EXPORT, // TODO: To move to SECTION_KEYBACKUP
|
||||
ADVANCED_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
DEBUG_CRYPTO_INFO,
|
||||
DEBUG_CROSSSIGNING_INFO,
|
||||
DEBUG_CROSSSIGNING_BOOTSTRAP,
|
||||
DEBUG_COUNT
|
||||
};
|
||||
|
||||
|
||||
@interface SecurityViewController () <
|
||||
SettingsKeyBackupTableViewSectionDelegate,
|
||||
KeyBackupSetupCoordinatorBridgePresenterDelegate,
|
||||
KeyBackupRecoverCoordinatorBridgePresenterDelegate,
|
||||
UIDocumentInteractionControllerDelegate>
|
||||
{
|
||||
// Current alert (if any).
|
||||
UIAlertController *currentAlert;
|
||||
|
||||
// Devices
|
||||
NSMutableArray<MXDevice *> *devicesArray;
|
||||
|
||||
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
||||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
|
||||
// The view used to export e2e keys
|
||||
MXKEncryptionKeysExportView *exportView;
|
||||
|
||||
// The document interaction Controller used to export e2e keys
|
||||
UIDocumentInteractionController *documentInteractionController;
|
||||
NSURL *keyExportsFile;
|
||||
NSTimer *keyExportsFileDeletionTimer;
|
||||
|
||||
// The current pushed view controller
|
||||
UIViewController *pushedViewController;
|
||||
|
||||
SettingsKeyBackupTableViewSection *keyBackupSection;
|
||||
KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter;
|
||||
KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SecurityViewController
|
||||
|
||||
#pragma mark - Setup & Teardown
|
||||
|
||||
+ (SecurityViewController*)instantiateWithMatrixSession:(MXSession*)matrixSession
|
||||
{
|
||||
SecurityViewController* viewController = [[UIStoryboard storyboardWithName:@"Security" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
|
||||
[viewController addMatrixSession:matrixSession];
|
||||
return viewController;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - View life cycle
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
|
||||
self.navigationItem.title = NSLocalizedStringFromTable(@"security_settings_title", @"Vector", nil);
|
||||
|
||||
// Remove back bar button title when pushing a view controller
|
||||
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
||||
|
||||
[self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
|
||||
[self.tableView registerNib:MXKTableViewCellWithTextView.nib forCellReuseIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
|
||||
|
||||
// Enable self sizing cells
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 50;
|
||||
|
||||
if (self.mainSession.crypto.backup)
|
||||
{
|
||||
MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId
|
||||
deviceId:self.mainSession.matrixRestClient.credentials.deviceId];
|
||||
|
||||
if (deviceInfo)
|
||||
{
|
||||
keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo];
|
||||
keyBackupSection.delegate = self;
|
||||
}
|
||||
}
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
}];
|
||||
[self userInterfaceThemeDidChange];
|
||||
}
|
||||
|
||||
- (void)userInterfaceThemeDidChange
|
||||
{
|
||||
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
||||
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
// Check the table view style to select its bg color.
|
||||
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
|
||||
self.view.backgroundColor = self.tableView.backgroundColor;
|
||||
self.tableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return ThemeService.shared.theme.statusBarStyle;
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
if (documentInteractionController)
|
||||
{
|
||||
[documentInteractionController dismissPreviewAnimated:NO];
|
||||
[documentInteractionController dismissMenuAnimated:NO];
|
||||
documentInteractionController = nil;
|
||||
}
|
||||
|
||||
if (kThemeServiceDidChangeThemeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
|
||||
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
||||
}
|
||||
|
||||
keyBackupSetupCoordinatorBridgePresenter = nil;
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Security"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
// Refresh display
|
||||
[self reloadData];
|
||||
|
||||
// Refresh the current device information in parallel
|
||||
[self loadCurrentDeviceInformation];
|
||||
|
||||
// Refresh devices in parallel
|
||||
[self loadDevices];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
currentAlert = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)pushViewController:(UIViewController*)viewController
|
||||
{
|
||||
// Keep ref on pushed view controller
|
||||
pushedViewController = viewController;
|
||||
|
||||
// Hide back button title
|
||||
self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
||||
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
|
||||
- (void)releasePushedViewController
|
||||
{
|
||||
if (pushedViewController)
|
||||
{
|
||||
if ([pushedViewController isKindOfClass:[UINavigationController class]])
|
||||
{
|
||||
UINavigationController *navigationController = (UINavigationController*)pushedViewController;
|
||||
for (id subViewController in navigationController.viewControllers)
|
||||
{
|
||||
if ([subViewController respondsToSelector:@selector(destroy)])
|
||||
{
|
||||
[subViewController destroy];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([pushedViewController respondsToSelector:@selector(destroy)])
|
||||
{
|
||||
[(id)pushedViewController destroy];
|
||||
}
|
||||
|
||||
pushedViewController = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
// Remove observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)loadCurrentDeviceInformation
|
||||
{
|
||||
// Refresh the current device information
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
[account loadDeviceInformation:^{
|
||||
|
||||
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self reloadData];
|
||||
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)cryptographyInformation
|
||||
{
|
||||
// TODO Handle multi accounts
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
|
||||
// Crypto information
|
||||
NSMutableAttributedString *cryptoInformationString = [[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_name", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}];
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:account.device.displayName ? account.device.displayName : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_id", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:account.device.deviceId ? account.device.deviceId : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_key", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
NSString *fingerprint = account.mxSession.crypto.deviceEd25519Key;
|
||||
if (fingerprint)
|
||||
{
|
||||
fingerprint = [MXTools addWhiteSpacesToString:fingerprint every:4];
|
||||
}
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:fingerprint ? fingerprint : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}]];
|
||||
|
||||
return cryptoInformationString;
|
||||
}
|
||||
|
||||
- (NSAttributedString*)crossSigningStatus
|
||||
{
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
MXCrossSigning *crossSigning = account.mxSession.crypto.crossSigning;
|
||||
MXCrossSigningInfo *myUserCrossSigningKeys = crossSigning.myUserCrossSigningKeys;
|
||||
|
||||
// Crypto information
|
||||
NSMutableAttributedString *cryptoInformationString = [NSMutableAttributedString new];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:@"Cross-Signing\n"
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}]];
|
||||
|
||||
|
||||
NSString *crossSigningEnabled = [NSString stringWithFormat:@"Cross-signing is %@.\n",
|
||||
crossSigning.isBootstrapped ? @"enabled" :
|
||||
myUserCrossSigningKeys ? @"enabled in read-only" : @"disabled"];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:crossSigningEnabled
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
|
||||
NSString *crossSigningKeysTrust = [NSString stringWithFormat:@"Keys are %@.\n",
|
||||
myUserCrossSigningKeys.trustLevel.isVerified ? @"trusted" : @"not trusted"];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:crossSigningKeysTrust
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
return cryptoInformationString;
|
||||
}
|
||||
|
||||
- (void)loadDevices
|
||||
{
|
||||
// Refresh the account devices list
|
||||
MXWeakify(self);
|
||||
[self.mainSession.matrixRestClient devices:^(NSArray<MXDevice *> *devices) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (devices)
|
||||
{
|
||||
self->devicesArray = [NSMutableArray arrayWithArray:devices];
|
||||
|
||||
// Sort devices according to the last seen date.
|
||||
NSComparator comparator = ^NSComparisonResult(MXDevice *deviceA, MXDevice *deviceB) {
|
||||
|
||||
if (deviceA.lastSeenTs > deviceB.lastSeenTs)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
if (deviceA.lastSeenTs < deviceB.lastSeenTs)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
return NSOrderedSame;
|
||||
};
|
||||
|
||||
// Sort devices list
|
||||
[self->devicesArray sortUsingComparator:comparator];
|
||||
}
|
||||
else
|
||||
{
|
||||
self->devicesArray = nil;
|
||||
|
||||
}
|
||||
|
||||
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self reloadData];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
// Display the data that has been loaded last time
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self reloadData];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
// Trigger a full table reloadData
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - Segues
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
// Keep ref on destinationViewController
|
||||
[super prepareForSegue:segue sender:sender];
|
||||
|
||||
// FIXME add night mode
|
||||
}
|
||||
|
||||
#pragma mark - UITableView data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return SECTION_COUNT;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_CRYPTO_SESSIONS:
|
||||
count = devicesArray.count + 1;
|
||||
break;
|
||||
case SECTION_KEYBACKUP:
|
||||
count = keyBackupSection.numberOfRows;
|
||||
break;
|
||||
case SECTION_ADVANCED:
|
||||
count = ADVANCED_COUNT;
|
||||
break;
|
||||
case SECTION_DEBUG:
|
||||
count = DEBUG_COUNT;
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithLabelAndSwitch*)getLabelAndSwitchCell:(UITableView*)tableview forIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch *cell = [tableview dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left;
|
||||
cell.mxkSwitchTrailingConstraint.constant = 15;
|
||||
|
||||
cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
[cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// Force layout before reusing a cell (fix switch displayed outside the screen)
|
||||
[cell layoutIfNeeded];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCell*)getDefaultTableViewCell:(UITableView*)tableView
|
||||
{
|
||||
MXKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
|
||||
if (!cell)
|
||||
{
|
||||
cell = [[MXKTableViewCell alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
cell.accessoryView = nil;
|
||||
cell.imageView.image = nil;
|
||||
}
|
||||
cell.textLabel.accessibilityIdentifier = nil;
|
||||
cell.textLabel.font = [UIFont systemFontOfSize:17];
|
||||
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
cell.contentView.backgroundColor = UIColor.clearColor;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MXKTableViewCell*)deviceCellWithDevice:(MXDevice*)device forTableView:(UITableView*)tableView
|
||||
{
|
||||
MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView];
|
||||
NSString *name = device.displayName;
|
||||
NSString *deviceId = device.deviceId;
|
||||
cell.textLabel.text = (name.length ? [NSString stringWithFormat:@"%@ (%@)", name, deviceId] : [NSString stringWithFormat:@"(%@)", deviceId]);
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
if ([deviceId isEqualToString:self.mainSession.matrixRestClient.credentials.deviceId])
|
||||
{
|
||||
cell.textLabel.font = [UIFont boldSystemFontOfSize:17];
|
||||
}
|
||||
|
||||
cell.imageView.image = [self shieldImageForDevice:deviceId];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (UIImage*)shieldImageForDevice:(NSString*)deviceId
|
||||
{
|
||||
UIImage* shieldImageForDevice = [UIImage imageNamed:@"encryption_warning"];
|
||||
MXDeviceInfo *device = [self.mainSession.crypto deviceWithDeviceId:deviceId ofUser:self.mainSession.myUser.userId];
|
||||
if (device.trustLevel.isVerified)
|
||||
{
|
||||
shieldImageForDevice = [UIImage imageNamed:@"encryption_trusted"];
|
||||
}
|
||||
|
||||
return shieldImageForDevice;
|
||||
}
|
||||
|
||||
|
||||
- (MXKTableViewCell*)descriptionCellForTableView:(UITableView*)tableView withText:(NSString*)text
|
||||
{
|
||||
MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView];
|
||||
cell.textLabel.text = text;
|
||||
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
cell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
- (MXKTableViewCellWithTextView*)textViewCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKTableViewCellWithTextView *textViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
textViewCell.mxkTextView.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
textViewCell.mxkTextView.font = [UIFont systemFontOfSize:17];
|
||||
textViewCell.mxkTextView.backgroundColor = [UIColor clearColor];
|
||||
textViewCell.mxkTextViewLeadingConstraint.constant = tableView.separatorInset.left;
|
||||
textViewCell.mxkTextViewTrailingConstraint.constant = tableView.separatorInset.right;
|
||||
textViewCell.mxkTextView.accessibilityIdentifier = nil;
|
||||
|
||||
return textViewCell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
// set the cell to a default value to avoid application crashes
|
||||
UITableViewCell *cell = [[UITableViewCell alloc] init];
|
||||
cell.backgroundColor = [UIColor redColor];
|
||||
|
||||
MXSession* session = self.mainSession;
|
||||
if (section == SECTION_CRYPTO_SESSIONS)
|
||||
{
|
||||
if (row < devicesArray.count)
|
||||
{
|
||||
cell = [self deviceCellWithDevice:devicesArray[row] forTableView:tableView];
|
||||
}
|
||||
else if (row == devicesArray.count)
|
||||
{
|
||||
cell = [self descriptionCellForTableView:tableView
|
||||
withText:NSLocalizedStringFromTable(@"security_settings_crypto_sessions_description", @"Vector", nil) ];
|
||||
|
||||
}
|
||||
}
|
||||
else if (section == SECTION_ADVANCED)
|
||||
{
|
||||
switch (row)
|
||||
{
|
||||
case ADVANCED_BLACKLIST_UNVERIFIED_DEVICES:
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"security_settings_blacklist_unverified_devices", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = session.crypto.globalBlacklistUnverifiedDevices;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
labelAndSwitchCell.mxkSwitch.enabled = YES;
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleBlacklistUnverifiedDevices:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
break;
|
||||
}
|
||||
case ADVANCED_BLACKLIST_UNVERIFIED_DEVICES_DESCRIPTION:
|
||||
{
|
||||
cell = [self descriptionCellForTableView:tableView
|
||||
withText:NSLocalizedStringFromTable(@"security_settings_blacklist_unverified_devices_description", @"Vector", nil) ];
|
||||
|
||||
break;
|
||||
}
|
||||
case ADVANCED_EXPORT:
|
||||
{
|
||||
MXKTableViewCellWithButton *exportKeysBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
if (!exportKeysBtnCell)
|
||||
{
|
||||
exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
exportKeysBtnCell.mxkButton.titleLabel.text = nil;
|
||||
exportKeysBtnCell.mxkButton.enabled = YES;
|
||||
}
|
||||
|
||||
NSString *btnTitle = NSLocalizedStringFromTable(@"security_settings_export_keys_manually", @"Vector", nil);
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
|
||||
[exportKeysBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
|
||||
exportKeysBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
|
||||
[exportKeysBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
|
||||
[exportKeysBtnCell.mxkButton addTarget:self action:@selector(exportEncryptionKeys:) forControlEvents:UIControlEventTouchUpInside];
|
||||
exportKeysBtnCell.mxkButton.accessibilityIdentifier = nil;
|
||||
|
||||
cell = exportKeysBtnCell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (section == SECTION_KEYBACKUP)
|
||||
{
|
||||
cell = [keyBackupSection cellForRowAtRow:row];
|
||||
}
|
||||
else if (section == SECTION_DEBUG)
|
||||
{
|
||||
switch (row)
|
||||
{
|
||||
case DEBUG_CRYPTO_INFO:
|
||||
{
|
||||
MXKTableViewCellWithTextView *cryptoCell = [self textViewCellForTableView:tableView atIndexPath:indexPath];
|
||||
cryptoCell.mxkTextView.attributedText = [self cryptographyInformation];
|
||||
cell = cryptoCell;
|
||||
break;
|
||||
}
|
||||
case DEBUG_CROSSSIGNING_INFO:
|
||||
{
|
||||
MXKTableViewCellWithTextView *cryptoCell = [self textViewCellForTableView:tableView atIndexPath:indexPath];
|
||||
cryptoCell.mxkTextView.attributedText = [self crossSigningStatus];
|
||||
cell = cryptoCell;
|
||||
break;
|
||||
}
|
||||
case DEBUG_CROSSSIGNING_BOOTSTRAP:
|
||||
{
|
||||
MXKTableViewCellWithButton *exportKeysBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
if (!exportKeysBtnCell)
|
||||
{
|
||||
exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
|
||||
NSString *btnTitle = @"Bootstrap cross-signing";
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
|
||||
[exportKeysBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
|
||||
exportKeysBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
|
||||
[exportKeysBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
|
||||
//[exportKeysBtnCell.mxkButton addTarget:self action:@selector(bootstrapCrossSigning:) forControlEvents:UIControlEventTouchUpInside];
|
||||
exportKeysBtnCell.mxkButton.accessibilityIdentifier = nil;
|
||||
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
exportKeysBtnCell.mxkButton.enabled = NO; //!crossSigning.myUserCrossSigningKeys;
|
||||
|
||||
cell = exportKeysBtnCell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case SECTION_CRYPTO_SESSIONS:
|
||||
return NSLocalizedStringFromTable(@"security_settings_crypto_sessions", @"Vector", nil);
|
||||
case SECTION_KEYBACKUP:
|
||||
return NSLocalizedStringFromTable(@"security_settings_backup", @"Vector", nil);
|
||||
case SECTION_ADVANCED:
|
||||
return NSLocalizedStringFromTable(@"security_settings_advanced", @"Vector", nil);
|
||||
case SECTION_DEBUG:
|
||||
return @"DEBUG";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
if ([view isKindOfClass:UITableViewHeaderFooterView.class])
|
||||
{
|
||||
// Customize label style
|
||||
UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view;
|
||||
tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
tableViewHeaderFooterView.textLabel.font = [UIFont systemFontOfSize:15];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UITableView delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
{
|
||||
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone)
|
||||
{
|
||||
// Update the selected background view
|
||||
if (ThemeService.shared.theme.selectedBackgroundColor)
|
||||
{
|
||||
cell.selectedBackgroundView = [[UIView alloc] init];
|
||||
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tableView.style == UITableViewStylePlain)
|
||||
{
|
||||
cell.selectedBackgroundView = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.selectedBackgroundView.backgroundColor = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == SECTION_CRYPTO_SESSIONS)
|
||||
{
|
||||
return 44;
|
||||
}
|
||||
return 24;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.tableView == tableView)
|
||||
{
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
if (section == SECTION_CRYPTO_SESSIONS)
|
||||
{
|
||||
NSUInteger deviceIndex = row;
|
||||
if (deviceIndex < devicesArray.count)
|
||||
{
|
||||
ManageSessionViewController *viewController = [ManageSessionViewController instantiateWithMatrixSession:self.mainSession andDevice:devicesArray[deviceIndex]];
|
||||
|
||||
[self pushViewController:viewController];
|
||||
}
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIDocumentInteractionControllerDelegate
|
||||
|
||||
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application
|
||||
{
|
||||
// If iOS wants to call this method, this is the right time to remove the file
|
||||
[self deleteKeyExportFile];
|
||||
}
|
||||
|
||||
- (void)documentInteractionControllerDidDismissOptionsMenu:(UIDocumentInteractionController *)controller
|
||||
{
|
||||
documentInteractionController = nil;
|
||||
}
|
||||
|
||||
#pragma mark - actions
|
||||
|
||||
- (void)exportEncryptionKeys:(UITapGestureRecognizer *)recognizer
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
exportView = [[MXKEncryptionKeysExportView alloc] initWithMatrixSession:self.mainSession];
|
||||
currentAlert = exportView.alertController;
|
||||
|
||||
// Use a temporary file for the export
|
||||
keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"riot-keys.txt"]];
|
||||
|
||||
// Make sure the file is empty
|
||||
[self deleteKeyExportFile];
|
||||
|
||||
// Show the export dialog
|
||||
MXWeakify(self);
|
||||
[exportView showInViewController:self toExportKeysToFile:keyExportsFile onComplete:^(BOOL success) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
self->currentAlert = nil;
|
||||
self->exportView = nil;
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Let another app handling this file
|
||||
self->documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:self->keyExportsFile];
|
||||
[self->documentInteractionController setDelegate:self];
|
||||
|
||||
if ([self->documentInteractionController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES])
|
||||
{
|
||||
// We want to delete the temp keys file after it has been processed by the other app.
|
||||
// We use [UIDocumentInteractionControllerDelegate didEndSendingToApplication] for that
|
||||
// but it is not reliable for all cases (see http://stackoverflow.com/a/21867096).
|
||||
// So, arm a timer to auto delete the file after 10mins.
|
||||
self->keyExportsFileDeletionTimer = [NSTimer scheduledTimerWithTimeInterval:600 target:self selector:@selector(deleteKeyExportFile) userInfo:self repeats:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
self->documentInteractionController = nil;
|
||||
[self deleteKeyExportFile];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteKeyExportFile
|
||||
{
|
||||
// Cancel the deletion timer if it is still here
|
||||
if (keyExportsFileDeletionTimer)
|
||||
{
|
||||
[keyExportsFileDeletionTimer invalidate];
|
||||
keyExportsFileDeletionTimer = nil;
|
||||
}
|
||||
|
||||
// And delete the file
|
||||
if (keyExportsFile && [[NSFileManager defaultManager] fileExistsAtPath:keyExportsFile.path])
|
||||
{
|
||||
[[NSFileManager defaultManager] removeItemAtPath:keyExportsFile.path error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleBlacklistUnverifiedDevices:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
self.mainSession.crypto.globalBlacklistUnverifiedDevices = switchButton.on;
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SettingsKeyBackupTableViewSectionDelegate
|
||||
|
||||
- (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithTextView *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection textCellForRow:(NSInteger)textCellForRow
|
||||
{
|
||||
return [self textViewCellForTableView:self.tableView atIndexPath:[NSIndexPath indexPathForRow:textCellForRow inSection:SECTION_KEYBACKUP]];
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithButton *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow
|
||||
{
|
||||
MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
|
||||
if (!cell)
|
||||
{
|
||||
cell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix https://github.com/vector-im/riot-ios/issues/1354
|
||||
cell.mxkButton.titleLabel.text = nil;
|
||||
cell.mxkButton.enabled = YES;
|
||||
}
|
||||
|
||||
cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
[cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackupTableViewSectionShowKeyBackupSetup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection
|
||||
{
|
||||
[self showKeyBackupSetupFromSignOutFlow:NO];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion
|
||||
{
|
||||
[self showKeyBackupRecover:keyBackupVersion];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion
|
||||
{
|
||||
MXWeakify(self);
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
currentAlert =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil)
|
||||
message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_button_delete", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion];
|
||||
}]];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showActivityIndicator:(BOOL)show
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showError:(NSError *)error
|
||||
{
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
|
||||
|
||||
- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow
|
||||
{
|
||||
keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
|
||||
[keyBackupSetupCoordinatorBridgePresenter presentFrom:self
|
||||
isStartedFromSignOut:showFromSignOutFlow
|
||||
animated:true];
|
||||
|
||||
keyBackupSetupCoordinatorBridgePresenter.delegate = self;
|
||||
}
|
||||
|
||||
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupSetupCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupSetupCoordinatorBridgePresenter = nil;
|
||||
|
||||
[keyBackupSection reload];
|
||||
}
|
||||
|
||||
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
|
||||
|
||||
- (void)showKeyBackupRecover:(MXKeyBackupVersion*)keyBackupVersion
|
||||
{
|
||||
keyBackupRecoverCoordinatorBridgePresenter = [[KeyBackupRecoverCoordinatorBridgePresenter alloc] initWithSession:self.mainSession keyBackupVersion:keyBackupVersion];
|
||||
|
||||
[keyBackupRecoverCoordinatorBridgePresenter presentFrom:self animated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter.delegate = self;
|
||||
}
|
||||
|
||||
- (void)keyBackupRecoverCoordinatorBridgePresenterDidCancel:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
|
@ -16,11 +16,9 @@
|
|||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "DeviceView.h"
|
||||
|
||||
#import "MediaPickerViewController.h"
|
||||
|
||||
@interface SettingsViewController : MXKTableViewController<UITextFieldDelegate, MXKDeviceViewDelegate, UIDocumentInteractionControllerDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate, MXKDataSourceDelegate>
|
||||
@interface SettingsViewController : MXKTableViewController<UITextFieldDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate, MXKDataSourceDelegate>
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#import "CountryPickerViewController.h"
|
||||
#import "LanguagePickerViewController.h"
|
||||
#import "DeactivateAccountViewController.h"
|
||||
#import "SecurityViewController.h"
|
||||
|
||||
#import "NBPhoneNumberUtil.h"
|
||||
#import "RageShakeManager.h"
|
||||
|
@ -51,6 +52,7 @@ enum
|
|||
{
|
||||
SETTINGS_SECTION_SIGN_OUT_INDEX = 0,
|
||||
SETTINGS_SECTION_USER_SETTINGS_INDEX,
|
||||
SETTINGS_SECTION_SECURITY_INDEX,
|
||||
SETTINGS_SECTION_NOTIFICATIONS_SETTINGS_INDEX,
|
||||
SETTINGS_SECTION_CALLS_INDEX,
|
||||
SETTINGS_SECTION_DISCOVERY_INDEX,
|
||||
|
@ -62,9 +64,6 @@ enum
|
|||
SETTINGS_SECTION_ADVANCED_INDEX,
|
||||
SETTINGS_SECTION_OTHER_INDEX,
|
||||
SETTINGS_SECTION_LABS_INDEX,
|
||||
SETTINGS_SECTION_CRYPTOGRAPHY_INDEX,
|
||||
SETTINGS_SECTION_KEYBACKUP_INDEX,
|
||||
SETTINGS_SECTION_DEVICES_INDEX,
|
||||
SETTINGS_SECTION_FLAIR_INDEX,
|
||||
SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX,
|
||||
SETTINGS_SECTION_COUNT
|
||||
|
@ -136,23 +135,14 @@ enum
|
|||
{
|
||||
LABS_USE_ROOM_MEMBERS_LAZY_LOADING_INDEX = 0,
|
||||
LABS_USE_JITSI_WIDGET_INDEX,
|
||||
LABS_CRYPTO_INDEX,
|
||||
LABS_COUNT, // TODO: Remove it once features exist
|
||||
LABS_DM_KEY_VERIFICATION_INDEX,
|
||||
LABS_CROSS_SIGNING_INDEX,
|
||||
// LABS_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTOGRAPHY_INFO_INDEX = 0,
|
||||
CRYPTOGRAPHY_BLACKLIST_UNVERIFIED_DEVICES_INDEX,
|
||||
CRYPTOGRAPHY_EXPORT_INDEX,
|
||||
CRYPTOGRAPHY_COUNT
|
||||
LABS_COUNT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DEVICES_DESCRIPTION_INDEX = 0
|
||||
SECURITY_BUTTON_INDEX = 0,
|
||||
SECURITY_COUNT
|
||||
};
|
||||
|
||||
#define SECTION_TITLE_PADDING_WHEN_HIDDEN 0.01f
|
||||
|
@ -161,10 +151,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
|
|||
|
||||
|
||||
@interface SettingsViewController () <DeactivateAccountViewControllerDelegate,
|
||||
SettingsKeyBackupTableViewSectionDelegate,
|
||||
MXKEncryptionInfoViewDelegate,
|
||||
KeyBackupSetupCoordinatorBridgePresenterDelegate,
|
||||
KeyBackupRecoverCoordinatorBridgePresenterDelegate,
|
||||
SignOutAlertPresenterDelegate,
|
||||
SingleImagePickerPresenterDelegate,
|
||||
SettingsDiscoveryTableViewSectionDelegate, SettingsDiscoveryViewModelCoordinatorDelegate,
|
||||
|
@ -223,10 +210,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
NSInteger localContactsSyncIndex;
|
||||
NSInteger localContactsPhoneBookCountryIndex;
|
||||
|
||||
// Devices
|
||||
NSMutableArray<MXDevice *> *devicesArray;
|
||||
DeviceView *deviceView;
|
||||
|
||||
// Flair: the groups data source
|
||||
GroupsDataSource *groupsDataSource;
|
||||
|
||||
|
@ -245,23 +228,13 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
//
|
||||
UIAlertController *resetPwdAlertController;
|
||||
|
||||
// The view used to export e2e keys
|
||||
MXKEncryptionKeysExportView *exportView;
|
||||
|
||||
// The document interaction Controller used to export e2e keys
|
||||
UIDocumentInteractionController *documentInteractionController;
|
||||
NSURL *keyExportsFile;
|
||||
NSTimer *keyExportsFileDeletionTimer;
|
||||
|
||||
BOOL keepNewEmailEditing;
|
||||
BOOL keepNewPhoneNumberEditing;
|
||||
|
||||
// The current pushed view controller
|
||||
UIViewController *pushedViewController;
|
||||
|
||||
SettingsKeyBackupTableViewSection *keyBackupSection;
|
||||
KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter;
|
||||
KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter;
|
||||
|
||||
SettingsIdentityServerCoordinatorBridgePresenter *identityServerSettingsCoordinatorBridgePresenter;
|
||||
}
|
||||
|
@ -361,18 +334,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[self addMatrixSession:mxSession];
|
||||
}
|
||||
|
||||
if (self.mainSession.crypto.backup)
|
||||
{
|
||||
MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId
|
||||
deviceId:self.mainSession.matrixRestClient.credentials.deviceId];
|
||||
|
||||
if (deviceInfo)
|
||||
{
|
||||
keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo];
|
||||
keyBackupSection.delegate = self;
|
||||
}
|
||||
}
|
||||
|
||||
[self setupDiscoverySection];
|
||||
|
||||
groupsDataSource = [[GroupsDataSource alloc] initWithMatrixSession:self.mainSession];
|
||||
|
@ -435,13 +396,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
if (documentInteractionController)
|
||||
{
|
||||
[documentInteractionController dismissPreviewAnimated:NO];
|
||||
[documentInteractionController dismissMenuAnimated:NO];
|
||||
documentInteractionController = nil;
|
||||
}
|
||||
|
||||
if (kThemeServiceDidChangeThemeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
|
||||
|
@ -470,7 +424,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}
|
||||
|
||||
keyBackupSetupCoordinatorBridgePresenter = nil;
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
identityServerSettingsCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
|
@ -506,12 +459,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
// Refresh linked emails and phone numbers in parallel
|
||||
[self loadAccount3PIDs];
|
||||
|
||||
// Refresh the current device information in parallel
|
||||
[self loadCurrentDeviceInformation];
|
||||
|
||||
// Refresh devices in parallel
|
||||
[self loadDevices];
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
@ -640,12 +587,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
onReadyToDestroyHandler = nil;
|
||||
|
||||
if (deviceView)
|
||||
{
|
||||
[deviceView removeFromSuperview];
|
||||
deviceView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setNewEmailEditingEnabled:(BOOL)newEmailEditingEnabled
|
||||
|
@ -994,171 +935,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)loadCurrentDeviceInformation
|
||||
{
|
||||
// Refresh the current device information
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
[account loadDeviceInformation:^{
|
||||
|
||||
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self refreshSettings];
|
||||
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)cryptographyInformation
|
||||
{
|
||||
// TODO Handle multi accounts
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
|
||||
// Crypto information
|
||||
NSMutableAttributedString *cryptoInformationString = [[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_name", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}];
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:account.device.displayName ? account.device.displayName : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_id", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:account.device.deviceId ? account.device.deviceId : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_key", @"Vector", nil)
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
|
||||
NSString *fingerprint = account.mxSession.crypto.deviceEd25519Key;
|
||||
if (fingerprint)
|
||||
{
|
||||
fingerprint = [MXTools addWhiteSpacesToString:fingerprint every:4];
|
||||
}
|
||||
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:fingerprint ? fingerprint : @""
|
||||
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}]];
|
||||
|
||||
return cryptoInformationString;
|
||||
}
|
||||
|
||||
- (void)loadDevices
|
||||
{
|
||||
// Refresh the account devices list
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
[account.mxRestClient devices:^(NSArray<MXDevice *> *devices) {
|
||||
|
||||
if (devices)
|
||||
{
|
||||
devicesArray = [NSMutableArray arrayWithArray:devices];
|
||||
|
||||
// Sort devices according to the last seen date.
|
||||
NSComparator comparator = ^NSComparisonResult(MXDevice *deviceA, MXDevice *deviceB) {
|
||||
|
||||
if (deviceA.lastSeenTs > deviceB.lastSeenTs)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
if (deviceA.lastSeenTs < deviceB.lastSeenTs)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
return NSOrderedSame;
|
||||
};
|
||||
|
||||
// Sort devices list
|
||||
[devicesArray sortUsingComparator:comparator];
|
||||
}
|
||||
else
|
||||
{
|
||||
devicesArray = nil;
|
||||
|
||||
}
|
||||
|
||||
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self refreshSettings];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
// Display the data that has been loaded last time
|
||||
// Note: The use of 'reloadData' handles the case where the account has been logged out.
|
||||
[self refreshSettings];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showDeviceDetails:(MXDevice *)device
|
||||
{
|
||||
[self dismissKeyboard];
|
||||
|
||||
deviceView = [[DeviceView alloc] initWithDevice:device andMatrixSession:self.mainSession];
|
||||
deviceView.delegate = self;
|
||||
|
||||
// Add the view and define edge constraints
|
||||
[self.tableView.superview addSubview:deviceView];
|
||||
[self.tableView.superview bringSubviewToFront:deviceView];
|
||||
|
||||
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:deviceView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:deviceView
|
||||
attribute:NSLayoutAttributeLeft
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeLeft
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:deviceView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:deviceView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[topConstraint, leftConstraint, widthConstraint, heightConstraint]];
|
||||
}
|
||||
|
||||
- (void)deviceView:(DeviceView*)theDeviceView presentAlertController:(UIAlertController *)alert
|
||||
{
|
||||
[self dismissKeyboard];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)dismissDeviceView:(MXKDeviceView *)theDeviceView didUpdate:(BOOL)isUpdated
|
||||
{
|
||||
[deviceView removeFromSuperview];
|
||||
deviceView = nil;
|
||||
|
||||
if (isUpdated)
|
||||
{
|
||||
[self loadDevices];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)editNewEmailTextField
|
||||
{
|
||||
if (newEmailTextField && ![newEmailTextField becomeFirstResponder])
|
||||
|
@ -1457,35 +1233,15 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
|
||||
{
|
||||
count = devicesArray.count;
|
||||
if (count)
|
||||
{
|
||||
// For some description (DEVICES_DESCRIPTION_INDEX)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
|
||||
{
|
||||
// Check whether this section is visible.
|
||||
if (self.mainSession.crypto)
|
||||
{
|
||||
count = CRYPTOGRAPHY_COUNT;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX)
|
||||
{
|
||||
// Check whether this section is visible.
|
||||
if (self.mainSession.crypto)
|
||||
{
|
||||
count = keyBackupSection.numberOfRows;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_SECURITY_INDEX)
|
||||
{
|
||||
count = SECURITY_COUNT;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -2430,45 +2186,14 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == LABS_CRYPTO_INDEX)
|
||||
{
|
||||
MXSession* session = [AppDelegate theDelegate].mxSessions[0];
|
||||
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_e2e_encryption", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = (nil != session.crypto);
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsEndToEndEncryption:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
if (session.crypto)
|
||||
{
|
||||
// Once crypto is enabled, it is enabled
|
||||
labelAndSwitchCell.mxkSwitch.enabled = NO;
|
||||
}
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == LABS_DM_KEY_VERIFICATION_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_dm_key_verification", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableDMKeyVerification;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsDMKeyVerification:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == LABS_CROSS_SIGNING_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_cross_signing", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_enable_cross_signing", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableCrossSigning;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
labelAndSwitchCell.mxkSwitch.enabled = YES;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsCrossSigning:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
|
@ -2501,93 +2226,16 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[groupWithSwitchCell.toggleButton addTarget:self action:@selector(toggleCommunityFlair:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
|
||||
else if (section == SETTINGS_SECTION_SECURITY_INDEX)
|
||||
{
|
||||
if (row == DEVICES_DESCRIPTION_INDEX)
|
||||
switch (row)
|
||||
{
|
||||
MXKTableViewCell *descriptionCell = [self getDefaultTableViewCell:tableView];
|
||||
descriptionCell.textLabel.text = NSLocalizedStringFromTable(@"settings_devices_description", @"Vector", nil);
|
||||
descriptionCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
descriptionCell.textLabel.font = [UIFont systemFontOfSize:15];
|
||||
descriptionCell.textLabel.numberOfLines = 0;
|
||||
descriptionCell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
descriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
cell = descriptionCell;
|
||||
case SECURITY_BUTTON_INDEX:
|
||||
cell = [self getDefaultTableViewCell:tableView];
|
||||
cell.textLabel.text = NSLocalizedStringFromTable(@"security_settings_title", @"Vector", nil);
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger deviceIndex = row - 1;
|
||||
|
||||
MXKTableViewCell *deviceCell = [self getDefaultTableViewCell:tableView];
|
||||
if (deviceIndex < devicesArray.count)
|
||||
{
|
||||
NSString *name = devicesArray[deviceIndex].displayName;
|
||||
NSString *deviceId = devicesArray[deviceIndex].deviceId;
|
||||
deviceCell.textLabel.text = (name.length ? [NSString stringWithFormat:@"%@ (%@)", name, deviceId] : [NSString stringWithFormat:@"(%@)", deviceId]);
|
||||
deviceCell.textLabel.numberOfLines = 0;
|
||||
|
||||
if ([deviceId isEqualToString:self.mainSession.matrixRestClient.credentials.deviceId])
|
||||
{
|
||||
deviceCell.textLabel.font = [UIFont boldSystemFontOfSize:17];
|
||||
}
|
||||
}
|
||||
|
||||
cell = deviceCell;
|
||||
}
|
||||
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
|
||||
{
|
||||
if (row == CRYPTOGRAPHY_INFO_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithTextView *cryptoCell = [self textViewCellForTableView:tableView atIndexPath:indexPath];
|
||||
|
||||
cryptoCell.mxkTextView.attributedText = [self cryptographyInformation];
|
||||
|
||||
cell = cryptoCell;
|
||||
}
|
||||
else if (row == CRYPTOGRAPHY_BLACKLIST_UNVERIFIED_DEVICES_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_crypto_blacklist_unverified_devices", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = account.mxSession.crypto.globalBlacklistUnverifiedDevices;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
labelAndSwitchCell.mxkSwitch.enabled = YES;
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleBlacklistUnverifiedDevices:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == CRYPTOGRAPHY_EXPORT_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithButton *exportKeysBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
if (!exportKeysBtnCell)
|
||||
{
|
||||
exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix https://github.com/vector-im/riot-ios/issues/1354
|
||||
exportKeysBtnCell.mxkButton.titleLabel.text = nil;
|
||||
}
|
||||
|
||||
NSString *btnTitle = NSLocalizedStringFromTable(@"settings_crypto_export", @"Vector", nil);
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
|
||||
[exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
|
||||
[exportKeysBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
|
||||
exportKeysBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
|
||||
[exportKeysBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
|
||||
[exportKeysBtnCell.mxkButton addTarget:self action:@selector(exportEncryptionKeys:) forControlEvents:UIControlEventTouchUpInside];
|
||||
exportKeysBtnCell.mxkButton.accessibilityIdentifier = nil;
|
||||
|
||||
cell = exportKeysBtnCell;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX)
|
||||
{
|
||||
cell = [keyBackupSection cellForRowAtRow:row];
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
|
||||
{
|
||||
|
@ -2685,29 +2333,9 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
return NSLocalizedStringFromTable(@"settings_flair", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
|
||||
else if (section == SETTINGS_SECTION_SECURITY_INDEX)
|
||||
{
|
||||
// Check whether this section is visible
|
||||
if (devicesArray.count > 0)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"settings_devices", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
|
||||
{
|
||||
// Check whether this section is visible
|
||||
if (self.mainSession.crypto)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX)
|
||||
{
|
||||
// Check whether this section is visible
|
||||
if (self.mainSession.crypto)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"settings_key_backup", @"Vector", nil);
|
||||
}
|
||||
return NSLocalizedStringFromTable(@"settings_security", @"Vector", nil);
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX)
|
||||
{
|
||||
|
@ -3033,17 +2661,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
|
||||
{
|
||||
if (row > DEVICES_DESCRIPTION_INDEX)
|
||||
{
|
||||
NSUInteger deviceIndex = row - 1;
|
||||
if (deviceIndex < devicesArray.count)
|
||||
{
|
||||
[self showDeviceDetails:devicesArray[deviceIndex]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_CONTACTS_INDEX)
|
||||
{
|
||||
if (row == localContactsPhoneBookCountryIndex)
|
||||
|
@ -3055,6 +2672,19 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[self pushViewController:countryPicker];
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_SECURITY_INDEX)
|
||||
{
|
||||
switch (row)
|
||||
{
|
||||
case SECURITY_BUTTON_INDEX:
|
||||
{
|
||||
SecurityViewController *securityViewController = [SecurityViewController instantiateWithMatrixSession:self.mainSession];
|
||||
|
||||
[self pushViewController:securityViewController];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
@ -3421,132 +3051,13 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}
|
||||
}
|
||||
|
||||
- (void)toggleLabsEndToEndEncryption:(id)sender
|
||||
{
|
||||
if (sender && [sender isKindOfClass:UISwitch.class])
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
|
||||
if (switchButton.isOn && !account.mxCredentials.deviceId.length)
|
||||
{
|
||||
// Prompt the user to log in again when no device id is available.
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
// Prompt user
|
||||
NSString *msg = NSLocalizedStringFromTable(@"settings_labs_e2e_encryption_prompt_message", @"Vector", nil);
|
||||
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:nil message:msg preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"later"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
// Reset toggle button
|
||||
[switchButton setOn:NO animated:YES];
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
|
||||
switchButton.enabled = NO;
|
||||
[self startActivityIndicator];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
|
||||
[[AppDelegate theDelegate] logoutWithConfirmation:NO completion:nil];
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier:@"SettingsVCEnableEncryptionAlert"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
|
||||
MXSession* session = [AppDelegate theDelegate].mxSessions[0];
|
||||
[session enableCrypto:switchButton.isOn success:^{
|
||||
|
||||
// When disabling crypto, reset the current device id as it cannot be reused.
|
||||
// This means that the user will need to log in again if he wants to re-enable e2e.
|
||||
if (!switchButton.isOn)
|
||||
{
|
||||
[account resetDeviceId];
|
||||
}
|
||||
|
||||
// Reload all data source of encrypted rooms
|
||||
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:session];
|
||||
|
||||
for (MXRoom *room in session.rooms)
|
||||
{
|
||||
if (room.summary.isEncrypted)
|
||||
{
|
||||
[roomDataSourceManager roomDataSourceForRoom:room.roomId create:NO onComplete:^(MXKRoomDataSource *roomDataSource) {
|
||||
[roomDataSource reload];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Once crypto is enabled, it is enabled
|
||||
switchButton.enabled = NO;
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Refresh table view to add cryptography information.
|
||||
[self.tableView reloadData];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Come back to previous state button
|
||||
[switchButton setOn:!switchButton.isOn animated:YES];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleLabsDMKeyVerification:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
RiotSettings.shared.enableDMKeyVerification = switchButton.isOn;
|
||||
}
|
||||
|
||||
- (void)toggleLabsCrossSigning:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
RiotSettings.shared.enableCrossSigning = switchButton.isOn;
|
||||
}
|
||||
|
||||
- (void)toggleBlacklistUnverifiedDevices:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
account.mxSession.crypto.globalBlacklistUnverifiedDevices = switchButton.on;
|
||||
|
||||
[self.tableView reloadData];
|
||||
self.mainSession.crypto.warnOnUnknowDevices = !RiotSettings.shared.enableCrossSigning;
|
||||
}
|
||||
|
||||
- (void)togglePinRoomsWithMissedNotif:(id)sender
|
||||
|
@ -4145,69 +3656,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
self.imagePickerPresenter = singleImagePickerPresenter;
|
||||
}
|
||||
|
||||
- (void)exportEncryptionKeys:(UITapGestureRecognizer *)recognizer
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
exportView = [[MXKEncryptionKeysExportView alloc] initWithMatrixSession:self.mainSession];
|
||||
currentAlert = exportView.alertController;
|
||||
|
||||
// Use a temporary file for the export
|
||||
keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"riot-keys.txt"]];
|
||||
|
||||
// Make sure the file is empty
|
||||
[self deleteKeyExportFile];
|
||||
|
||||
// Show the export dialog
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[exportView showInViewController:self toExportKeysToFile:keyExportsFile onComplete:^(BOOL success) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
self->exportView = nil;
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Let another app handling this file
|
||||
self->documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:keyExportsFile];
|
||||
[self->documentInteractionController setDelegate:self];
|
||||
|
||||
if ([self->documentInteractionController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES])
|
||||
{
|
||||
// We want to delete the temp keys file after it has been processed by the other app.
|
||||
// We use [UIDocumentInteractionControllerDelegate didEndSendingToApplication] for that
|
||||
// but it is not reliable for all cases (see http://stackoverflow.com/a/21867096).
|
||||
// So, arm a timer to auto delete the file after 10mins.
|
||||
keyExportsFileDeletionTimer = [NSTimer scheduledTimerWithTimeInterval:600 target:self selector:@selector(deleteKeyExportFile) userInfo:self repeats:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
self->documentInteractionController = nil;
|
||||
[self deleteKeyExportFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteKeyExportFile
|
||||
{
|
||||
// Cancel the deletion timer if it is still here
|
||||
if (keyExportsFileDeletionTimer)
|
||||
{
|
||||
[keyExportsFileDeletionTimer invalidate];
|
||||
keyExportsFileDeletionTimer = nil;
|
||||
}
|
||||
|
||||
// And delete the file
|
||||
if (keyExportsFile && [[NSFileManager defaultManager] fileExistsAtPath:keyExportsFile.path])
|
||||
{
|
||||
[[NSFileManager defaultManager] removeItemAtPath:keyExportsFile.path error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showThemePicker
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
@ -4569,18 +4017,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[self presentViewController:resetPwdAlertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIDocumentInteractionControllerDelegate
|
||||
|
||||
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application
|
||||
{
|
||||
// If iOS wants to call this method, this is the right time to remove the file
|
||||
[self deleteKeyExportFile];
|
||||
}
|
||||
|
||||
- (void)documentInteractionControllerDidDismissOptionsMenu:(UIDocumentInteractionController *)controller
|
||||
{
|
||||
documentInteractionController = nil;
|
||||
}
|
||||
|
||||
#pragma mark - MXKCountryPickerViewControllerDelegate
|
||||
|
||||
|
@ -4664,96 +4100,8 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
[deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - SettingsKeyBackupTableViewSectionDelegate
|
||||
|
||||
- (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithTextView *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection textCellForRow:(NSInteger)textCellForRow
|
||||
{
|
||||
return [self textViewCellForTableView:self.tableView atIndexPath:[NSIndexPath indexPathForRow:textCellForRow inSection:SETTINGS_SECTION_KEYBACKUP_INDEX]];
|
||||
}
|
||||
|
||||
- (MXKTableViewCellWithButton *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow
|
||||
{
|
||||
MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
|
||||
|
||||
if (!cell)
|
||||
{
|
||||
cell = [[MXKTableViewCellWithButton alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix https://github.com/vector-im/riot-ios/issues/1354
|
||||
cell.mxkButton.titleLabel.text = nil;
|
||||
}
|
||||
|
||||
cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
[cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackupTableViewSectionShowKeyBackupSetup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection
|
||||
{
|
||||
[self showKeyBackupSetupFromSignOutFlow:NO];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion
|
||||
{
|
||||
[self showKeyBackupRecover:keyBackupVersion];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion
|
||||
{
|
||||
MXWeakify(self);
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
currentAlert =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil)
|
||||
message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_button_delete", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion];
|
||||
}]];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showActivityIndicator:(BOOL)show
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showError:(NSError *)error
|
||||
{
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
|
||||
#pragma mark - KeyBackupSetupCoordinatorBridgePresenter
|
||||
|
||||
- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow
|
||||
{
|
||||
|
@ -4774,28 +4122,6 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupSetupCoordinatorBridgePresenter = nil;
|
||||
|
||||
[keyBackupSection reload];
|
||||
}
|
||||
|
||||
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
|
||||
|
||||
- (void)showKeyBackupRecover:(MXKeyBackupVersion*)keyBackupVersion
|
||||
{
|
||||
keyBackupRecoverCoordinatorBridgePresenter = [[KeyBackupRecoverCoordinatorBridgePresenter alloc] initWithSession:self.mainSession keyBackupVersion:keyBackupVersion];
|
||||
|
||||
[keyBackupRecoverCoordinatorBridgePresenter presentFrom:self animated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter.delegate = self;
|
||||
}
|
||||
|
||||
- (void)keyBackupRecoverCoordinatorBridgePresenterDidCancel:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter {
|
||||
[keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true];
|
||||
keyBackupRecoverCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - SignOutAlertPresenterDelegate
|
||||
|
|