Merge pull request #2902 from vector-im/riot_2851
Notification in DMs / new Notification type
|
@ -129,6 +129,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 */; };
|
||||
|
@ -175,6 +181,11 @@
|
|||
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 */; };
|
||||
|
@ -531,6 +542,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 */; };
|
||||
|
@ -806,6 +822,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>"; };
|
||||
|
@ -852,6 +874,11 @@
|
|||
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>"; };
|
||||
|
@ -1393,6 +1420,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>"; };
|
||||
|
@ -1911,6 +1943,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 = (
|
||||
|
@ -2395,6 +2437,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 +2482,7 @@
|
|||
B1B5567620EE6C4C00210D55 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1A12C64239AB74500AA2B86 /* CrossSigning */,
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */,
|
||||
32DB556722FDADE50016329E /* ServiceTerms */,
|
||||
3232AB94225730E100AD6A5C /* DeviceVerification */,
|
||||
|
@ -3249,6 +3319,8 @@
|
|||
B1B5583F20EF768E00210D55 /* BubbleCells */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B108932623ABE82C00802670 /* BaseContentViews */,
|
||||
B1A12C65239ABBDB00AA2B86 /* KeyVerification */,
|
||||
B1B5584220EF768E00210D55 /* Encryption */,
|
||||
B1B5589220EF768E00210D55 /* RoomEmptyBubbleCell.h */,
|
||||
B1B558B620EF768E00210D55 /* RoomEmptyBubbleCell.m */,
|
||||
|
@ -4176,6 +4248,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 +4271,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 */,
|
||||
|
@ -4481,6 +4555,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 */,
|
||||
|
@ -4521,6 +4596,7 @@
|
|||
B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */,
|
||||
B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.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 +4611,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 +4647,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 */,
|
||||
|
@ -4619,6 +4698,7 @@
|
|||
32863A5C2384074C00D07C4A /* RiotSettingAllowedWidgets.swift in Sources */,
|
||||
B1B9DEDA22E9B7350065E677 /* SerializationService.swift in Sources */,
|
||||
B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */,
|
||||
B1C543AE23A286A000DCA1FA /* KeyVerificationRequestStatusBubbleCell.swift in Sources */,
|
||||
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */,
|
||||
B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */,
|
||||
B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */,
|
||||
|
@ -4634,6 +4714,7 @@
|
|||
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 */,
|
||||
|
@ -4652,10 +4733,12 @@
|
|||
B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */,
|
||||
B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */,
|
||||
B1DCC62822E60CE300625807 /* EmojiCategory.swift in Sources */,
|
||||
B14084CC23BF9DE90010F692 /* KeyVerificationConclusionWithPaginationTitleBubbleCell.swift in Sources */,
|
||||
B1B5578F20EF568D00210D55 /* GroupTableViewCell.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 */,
|
||||
|
@ -4709,6 +4792,7 @@
|
|||
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 */,
|
||||
|
@ -4734,6 +4818,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,6 +4830,7 @@
|
|||
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 */,
|
||||
|
@ -4802,11 +4888,13 @@
|
|||
B1B5577D20EE84BF00210D55 /* CircleButton.m in Sources */,
|
||||
32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */,
|
||||
B109D6F1222D8C400061B6D9 /* UIApplication.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 */,
|
||||
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */,
|
||||
B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */,
|
||||
|
|
|
@ -130,6 +130,15 @@ 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;
|
||||
|
||||
#pragma mark - Matrix Accounts handling
|
||||
|
||||
|
|
|
@ -4826,6 +4826,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);
|
||||
|
|
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 |
|
@ -1087,3 +1087,23 @@
|
|||
// 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";
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -1470,6 +1470,54 @@ internal enum VectorL10n {
|
|||
internal static var keyBackupSetupTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_setup_title")
|
||||
}
|
||||
/// 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 cancel
|
||||
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")
|
||||
}
|
||||
/// %.1fK
|
||||
internal static func largeBadgeValueKFormat(_ p1: Float) -> String {
|
||||
return VectorL10n.tr("Vector", "large_badge_value_k_format", p1)
|
||||
|
|
|
@ -31,6 +31,7 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
|||
private let otherDeviceId: String
|
||||
|
||||
private var incomingTransaction: MXIncomingSASTransaction?
|
||||
private var incomingKeyVerificationRequest: MXKeyVerificationRequest?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -66,12 +67,29 @@ 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
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let rootCoordinator = self.createDataLoadingScreenCoordinator()
|
||||
let rootCoordinator: Coordinator & Presentable
|
||||
|
||||
if let incomingKeyVerificationRequest = self.incomingKeyVerificationRequest {
|
||||
rootCoordinator = self.createDataLoadingScreenCoordinator(with: incomingKeyVerificationRequest)
|
||||
} else {
|
||||
rootCoordinator = self.createDataLoadingScreenCoordinator()
|
||||
}
|
||||
|
||||
rootCoordinator.start()
|
||||
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
|
@ -91,6 +109,14 @@ 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 showStart(otherUser: MXUser, otherDevice: MXDeviceInfo) {
|
||||
let coordinator = DeviceVerificationStartCoordinator(session: self.session, otherUser: otherUser, otherDevice: otherDevice)
|
||||
|
@ -141,6 +167,16 @@ extension DeviceVerificationCoordinator: DeviceVerificationDataLoadingCoordinato
|
|||
self.showStart(otherUser: user, otherDevice: device)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -73,6 +73,18 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
let deviceVerificationDataLoadingViewModel = DeviceVerificationDataLoadingViewModel(session: session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
let deviceVerificationDataLoadingViewController = DeviceVerificationDataLoadingViewController.instantiate(with: deviceVerificationDataLoadingViewModel)
|
||||
self.deviceVerificationDataLoadingViewModel = deviceVerificationDataLoadingViewModel
|
||||
self.deviceVerificationDataLoadingViewController = deviceVerificationDataLoadingViewController
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -112,9 +112,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
|
||||
|
@ -60,40 +75,74 @@ 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) 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))
|
||||
}
|
||||
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
sself.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
}
|
||||
|
||||
let finalError = error ?? DeviceVerificationDataLoadingViewModelError.unknown
|
||||
|
||||
sself.update(viewState: .error(finalError))
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let finalError = error ?? DeviceVerificationDataLoadingViewModelError.unknown
|
||||
|
||||
sself.update(viewState: .error(finalError))
|
||||
})
|
||||
|
||||
|
||||
} else {
|
||||
self.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,27 +62,35 @@ 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;
|
||||
case MXEventTypeRoomMember:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (event.eventType == MXEventTypeRoomCreate)
|
||||
{
|
||||
MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:event.content];
|
||||
|
||||
if (createContent.roomPredecessorInfo)
|
||||
{
|
||||
self.tag = RoomBubbleCellDataTagRoomCreateWithPredecessor;
|
||||
}
|
||||
}
|
||||
[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 {
|
||||
|
|
|
@ -62,4 +62,26 @@
|
|||
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
|
||||
|
|
|
@ -35,6 +35,15 @@
|
|||
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;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomDataSource
|
||||
|
@ -71,6 +80,9 @@
|
|||
[self reload];
|
||||
|
||||
}];
|
||||
|
||||
[self registerKeyVerificationRequestNotification];
|
||||
[self registerDeviceVerificationTransactionNotification];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -117,6 +129,16 @@
|
|||
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
||||
}
|
||||
|
||||
if (self.keyVerificationRequestDidChangeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.keyVerificationRequestDidChangeNotificationObserver];
|
||||
}
|
||||
|
||||
if (self.deviceVerificationTransactionDidChangeNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.deviceVerificationTransactionDidChangeNotificationObserver];
|
||||
}
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
|
@ -189,6 +211,8 @@
|
|||
{
|
||||
roomBubbleCellData.senderAvatarPlaceholder = [AvatarGenerator generateAvatarForMatrixItem:roomBubbleCellData.senderId withDisplayName:roomBubbleCellData.senderDisplayName];
|
||||
}
|
||||
|
||||
[self updateKeyVerificationIfNeededForRoomBubbleCellData:roomBubbleCellData];
|
||||
|
||||
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
|
||||
|
@ -284,7 +308,7 @@
|
|||
]];
|
||||
}
|
||||
|
||||
MXKReceiptSendersContainer* avatarsContainer;
|
||||
MXKReceiptSendersContainer* avatarsContainer;
|
||||
|
||||
// Handle read receipts (if any)
|
||||
if (self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed)
|
||||
|
@ -349,47 +373,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 +550,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 +768,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
|
||||
|
||||
|
|
|
@ -353,6 +353,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;
|
||||
|
@ -2039,6 +2047,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 +2275,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)
|
||||
|
@ -5303,8 +5347,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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -15,3 +15,5 @@
|
|||
#import "EventFormatter.h"
|
||||
#import "MediaPickerViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "RoomBubbleCellData.h"
|
||||
#import "MXKRoomBubbleTableViewCell+Riot.h"
|
||||
|
|
|
@ -143,6 +143,13 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
|
|||
}
|
||||
}
|
||||
|
||||
// Make event types MXEventTypeKeyVerificationCancel and MXEventTypeKeyVerificationDone visible in timeline.
|
||||
// TODO: Find another way to keep them visible and avoid instantiate empty NSMutableAttributedString.
|
||||
if (event.eventType == MXEventTypeKeyVerificationCancel || event.eventType == MXEventTypeKeyVerificationDone)
|
||||
{
|
||||
return [NSMutableAttributedString new];
|
||||
}
|
||||
|
||||
NSAttributedString *attributedString = [super attributedStringFromEvent:event withRoomState:roomState error:error];
|
||||
|
||||
if (event.sentState == MXEventSentStateSent
|
||||
|
|