Merge remote-tracking branch 'origin/complete_security_allow_device_verification_retry' into complete_security_allow_device_verification_retry

This commit is contained in:
manuroe 2020-09-25 10:14:27 +02:00
commit e31cc51416
26 changed files with 1076 additions and 363 deletions

View file

@ -2,52 +2,53 @@ Changes to be released in next version
=================================================
✨ Features
*
*
🙌 Improvements
* Pin: Implement not allowed PINs feature. There is no restriction by default.
* Room: New Room Settings screen.
* Complete Security: Come back to the root screen if device verification is cancelled.
* Architecture: Use coordinator pattern for legacy screen flows (#3597).
🐛 Bugfix
* Timeline: Hide encrypted history (pre-invite) (#3660).
* Fix floating action buttons' images.
⚠️ API Changes
*
*
🗣 Translations
*
*
🧱 Build
*
*
Others
*
*
Changes in 1.0.12 (2020-09-16)
=================================================
✨ Features
*
*
🙌 Improvements
*
*
🐛 Bugfix
*
*
⚠️ API Changes
*
*
🗣 Translations
*
*
🧱 Build
*
*
Others
*
*
Improvements:
* Upgrade MatrixKit version ([v0.12.20](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.12.20)).
@ -56,10 +57,10 @@ Changes in 1.0.11 (2020-09-15)
=================================================
✨ Features
*
*
🙌 Improvements
* Room: Collapse state messages on room creation (#3629).
* Room: Collapse state messages on room creation (#3629).
* AuthVC: Make force PIN working for registration as well.
* AppDelegate: Do not show incoming key verification requests while authenticating.
@ -68,13 +69,13 @@ Changes in 1.0.11 (2020-09-15)
* Loading animation: Fix the bug where, after authentication, the animation disappeared too early and made auth screen flashed.
⚠️ API Changes
*
*
🗣 Translations
*
*
🧱 Build
*
*
Others
* buildRelease.sh: Pass a `git_tag` parameter to fastlane because fastlane `git_branch` method can fail.
@ -86,28 +87,28 @@ Changes in 1.0.10 (2020-09-08)
=================================================
✨ Features
*
*
🙌 Improvements
* AppDelegate: Convert to Swift (#3594).
* Contextualize floating button actions per tab (#3627).
🐛 Bugfix
* Show pin code screen on every foreground (#3620).
* Show pin code screen on every foreground (#3620).
* Close keyboard on pin code screen (#3622).
* Fix content leakage on pin code protection (#3624).
⚠️ API Changes
*
*
🗣 Translations
*
*
🧱 Build
* buildRelease.sh: Make sure it works for both branches and tags
Others
*
*
Improvements:
* Upgrade MatrixKit version ([v0.12.18](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.12.18)).

View file

@ -186,6 +186,13 @@
B10A3E8E24FE4368007C380F /* ElementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E8B24FE4367007C380F /* ElementView.swift */; };
B10A3E8F24FE4368007C380F /* Timeline_1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E8C24FE4367007C380F /* Timeline_1.swift */; };
B10A3E9024FE4368007C380F /* ElementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E8D24FE4367007C380F /* ElementViewController.swift */; };
B10A3E9324FE8254007C380F /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9124FE8254007C380F /* AppCoordinator.swift */; };
B10A3E9424FE8254007C380F /* AppCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9224FE8254007C380F /* AppCoordinatorType.swift */; };
B10A3E9824FE86AF007C380F /* SplitViewCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9624FE86AE007C380F /* SplitViewCoordinatorType.swift */; };
B10A3E9924FE86AF007C380F /* SplitViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9724FE86AF007C380F /* SplitViewCoordinator.swift */; };
B10A3E9C24FE88CB007C380F /* RootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9A24FE88CA007C380F /* RootRouter.swift */; };
B10A3E9D24FE88CB007C380F /* RootRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9B24FE88CB007C380F /* RootRouterType.swift */; };
B10A3E9F24FFC3E6007C380F /* PlaceholderDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10A3E9E24FFC3E5007C380F /* PlaceholderDetailViewController.swift */; };
B10CFBC32268D99D00A5842E /* JitsiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10CFBC22268D99D00A5842E /* JitsiService.swift */; };
B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1107EC72200B0720038014B /* KeyBackupRecoverSuccessViewController.swift */; };
B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1107EC92200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard */; };
@ -653,6 +660,9 @@
B1C562E5228C7C8D0037F12A /* RoomContextualMenuViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */; };
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */; };
B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */; };
B1C7822F2500EAF500337EB9 /* TabBarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C7822D2500EAF400337EB9 /* TabBarCoordinator.swift */; };
B1C782302500EAF500337EB9 /* TabBarCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C7822E2500EAF500337EB9 /* TabBarCoordinatorType.swift */; };
B1C782322500F96700337EB9 /* SplitViewPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C782312500F96600337EB9 /* SplitViewPresentable.swift */; };
B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2621EF6913000D1D89 /* UIViewController.swift */; };
B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; };
B1CE83B62422812100D07506 /* KeyVerificationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE83B52422812000D07506 /* KeyVerificationCoordinatorBridgePresenter.swift */; };
@ -1188,6 +1198,13 @@
B10A3E8B24FE4367007C380F /* ElementView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementView.swift; sourceTree = "<group>"; };
B10A3E8C24FE4367007C380F /* Timeline_1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timeline_1.swift; sourceTree = "<group>"; };
B10A3E8D24FE4367007C380F /* ElementViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementViewController.swift; sourceTree = "<group>"; };
B10A3E9124FE8254007C380F /* AppCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
B10A3E9224FE8254007C380F /* AppCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinatorType.swift; sourceTree = "<group>"; };
B10A3E9624FE86AE007C380F /* SplitViewCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitViewCoordinatorType.swift; sourceTree = "<group>"; };
B10A3E9724FE86AF007C380F /* SplitViewCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitViewCoordinator.swift; sourceTree = "<group>"; };
B10A3E9A24FE88CA007C380F /* RootRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = "<group>"; };
B10A3E9B24FE88CB007C380F /* RootRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouterType.swift; sourceTree = "<group>"; };
B10A3E9E24FFC3E5007C380F /* PlaceholderDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderDetailViewController.swift; sourceTree = "<group>"; };
B10CFBC22268D99D00A5842E /* JitsiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiService.swift; sourceTree = "<group>"; };
B1107EC72200B0720038014B /* KeyBackupRecoverSuccessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverSuccessViewController.swift; sourceTree = "<group>"; };
B1107EC92200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverSuccessViewController.storyboard; sourceTree = "<group>"; };
@ -1847,6 +1864,9 @@
B1C6FFE723954CE70055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
B1C6FFE823954D3B0055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
B1C6FFE923954D4B0055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Vector.strings; sourceTree = "<group>"; };
B1C7822D2500EAF400337EB9 /* TabBarCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarCoordinator.swift; sourceTree = "<group>"; };
B1C7822E2500EAF500337EB9 /* TabBarCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarCoordinatorType.swift; sourceTree = "<group>"; };
B1C782312500F96600337EB9 /* SplitViewPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewPresentable.swift; sourceTree = "<group>"; };
B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
B1CE83B52422812000D07506 /* KeyVerificationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
@ -2690,6 +2710,8 @@
B1098C0B21ED07E4000DDA48 /* Presentable.swift */,
B1098C0C21ED07E4000DDA48 /* NavigationRouterType.swift */,
B1098C0821ED07E4000DDA48 /* NavigationRouter.swift */,
B10A3E9B24FE88CB007C380F /* RootRouterType.swift */,
B10A3E9A24FE88CA007C380F /* RootRouter.swift */,
);
path = Routers;
sourceTree = "<group>";
@ -2704,6 +2726,19 @@
path = LoadingAnimation;
sourceTree = "<group>";
};
B10A3E9524FE86AE007C380F /* SplitView */ = {
isa = PBXGroup;
children = (
B10A3E9624FE86AE007C380F /* SplitViewCoordinatorType.swift */,
B10A3E9724FE86AF007C380F /* SplitViewCoordinator.swift */,
B1B556EC20EE6C4C00210D55 /* RiotSplitViewController.h */,
B1B556EB20EE6C4C00210D55 /* RiotSplitViewController.m */,
B10A3E9E24FFC3E5007C380F /* PlaceholderDetailViewController.swift */,
B1C782312500F96600337EB9 /* SplitViewPresentable.swift */,
);
path = SplitView;
sourceTree = "<group>";
};
B1107EC62200B0190038014B /* Success */ = {
isa = PBXGroup;
children = (
@ -3308,12 +3343,12 @@
children = (
ECF57A3725090C23004BBF9D /* CreateRoom */,
B1BB648724FD07B3008238AE /* Application */,
B10A3E9524FE86AE007C380F /* SplitView */,
EC1CA84F24C1DEC400DE9EBF /* SetPinCode */,
EC3B066324AC6ADD000DF9BF /* CrossSigning */,
EC711BB424A63C11008F830C /* AuthenticatedSession */,
EC711B9A24A63B58008F830C /* SecureBackup */,
EC711B4724A63B36008F830C /* Secrets */,
B1B556EA20EE6C4C00210D55 /* Main */,
B1560DA024B65A9500490F50 /* LaunchLoading */,
B1B556CA20EE6C4C00210D55 /* TabBar */,
B1B556F920EE6C4C00210D55 /* Authentication */,
@ -3614,6 +3649,8 @@
B1B556CA20EE6C4C00210D55 /* TabBar */ = {
isa = PBXGroup;
children = (
B1C7822E2500EAF500337EB9 /* TabBarCoordinatorType.swift */,
B1C7822D2500EAF400337EB9 /* TabBarCoordinator.swift */,
B1B556CB20EE6C4C00210D55 /* MasterTabBarController.h */,
B1B556CC20EE6C4C00210D55 /* MasterTabBarController.m */,
);
@ -3722,15 +3759,6 @@
path = People;
sourceTree = "<group>";
};
B1B556EA20EE6C4C00210D55 /* Main */ = {
isa = PBXGroup;
children = (
B1B556EC20EE6C4C00210D55 /* RiotSplitViewController.h */,
B1B556EB20EE6C4C00210D55 /* RiotSplitViewController.m */,
);
path = Main;
sourceTree = "<group>";
};
B1B556ED20EE6C4C00210D55 /* MediaPicker */ = {
isa = PBXGroup;
children = (
@ -4509,6 +4537,8 @@
B1BB649224FD428A008238AE /* AppDelegate.swift */,
F083BB0C1E7009EC00A9B29C /* LegacyAppDelegate.h */,
F083BB0D1E7009EC00A9B29C /* LegacyAppDelegate.m */,
B10A3E9224FE8254007C380F /* AppCoordinatorType.swift */,
B10A3E9124FE8254007C380F /* AppCoordinator.swift */,
);
path = Application;
sourceTree = "<group>";
@ -6206,12 +6236,14 @@
B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */,
B1BD71BC238E8F9600BA92E2 /* WidgetPermissionViewController.swift in Sources */,
B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
B10A3E9824FE86AF007C380F /* SplitViewCoordinatorType.swift in Sources */,
B11291EA238D35590077B478 /* SlidingModalPresentable.swift in Sources */,
B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */,
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
EC51E7AC2514D2E100AAE7DB /* RoomInfoListCoordinatorType.swift in Sources */,
B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */,
B10A3E9424FE8254007C380F /* AppCoordinatorType.swift in Sources */,
B108931F23AB80EF00802670 /* KeyVerificationIncomingRequestApprovalBubbleCell.swift in Sources */,
EC51E7922510C0A500AAE7DB /* SpanningSlidingModalContainerView.swift in Sources */,
B1CE83D42422817200D07506 /* KeyVerificationVerifiedViewController.swift in Sources */,
@ -6219,6 +6251,7 @@
B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */,
B1098BF621ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift in Sources */,
B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */,
B10A3E9D24FE88CB007C380F /* RootRouterType.swift in Sources */,
B1B9DEDC22E9B7440065E677 /* SerializationServiceType.swift in Sources */,
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */,
32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */,
@ -6249,6 +6282,8 @@
3291DC8D23E0BFF10009732F /* SecurityViewController.m in Sources */,
B18DEDD4243377C10075FEF7 /* KeyVerificationSelfVerifyWaitViewModelType.swift in Sources */,
B1C45A88232A8C2600165425 /* SettingsIdentityServerViewState.swift in Sources */,
B1C782302500EAF500337EB9 /* TabBarCoordinatorType.swift in Sources */,
B1C782322500F96700337EB9 /* SplitViewPresentable.swift in Sources */,
B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */,
32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */,
B1B558E120EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.m in Sources */,
@ -6355,6 +6390,7 @@
B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */,
ECF57A50250913E4004BBF9D /* MXKTableViewCellWithLabelAndSwitch.swift in Sources */,
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */,
B10A3E9324FE8254007C380F /* AppCoordinator.swift in Sources */,
B1B5572A20EE6C4D00210D55 /* RoomMemberDetailsViewController.m in Sources */,
EC3B066C24AC6ADE000DF9BF /* CrossSigningSetupBannerCell.swift in Sources */,
ECF57A4E25090C23004BBF9D /* EnterNewRoomDetailsViewModelType.swift in Sources */,
@ -6441,8 +6477,10 @@
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */,
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */,
B10A3E9924FE86AF007C380F /* SplitViewCoordinator.swift in Sources */,
B1BEE72A23DF38B20003A4CB /* UserVerificationSessionStatusCell.swift in Sources */,
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */,
B1C7822F2500EAF500337EB9 /* TabBarCoordinator.swift in Sources */,
B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */,
B1B5574020EE6C4D00210D55 /* SegmentedViewController.m in Sources */,
ECF57A4C25090C23004BBF9D /* EnterNewRoomDetailsViewAction.swift in Sources */,
@ -6656,6 +6694,7 @@
B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */,
B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */,
EC711B8924A63B37008F830C /* SecretsRecoveryWithPassphraseViewModel.swift in Sources */,
B10A3E9F24FFC3E6007C380F /* PlaceholderDetailViewController.swift in Sources */,
B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */,
EC711B7A24A63B37008F830C /* SecretsSetupRecoveryKeyViewAction.swift in Sources */,
B12D79FB23E2462200FACEDC /* UserVerificationStartCoordinator.swift in Sources */,
@ -6761,6 +6800,7 @@
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */,
B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */,
EC711B8624A63B37008F830C /* SecretsRecoveryWithPassphraseCoordinatorType.swift in Sources */,
B10A3E9C24FE88CB007C380F /* RootRouter.swift in Sources */,
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */,
B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */,
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */,

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
@ -261,7 +261,7 @@
<!--Master Tab Bar Controller-->
<scene sceneID="iBe-Y5-gBt">
<objects>
<tabBarController id="xaQ-tG-rlO" customClass="MasterTabBarController" sceneMemberID="viewController">
<tabBarController storyboardIdentifier="MasterTabBarController" id="xaQ-tG-rlO" customClass="MasterTabBarController" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="m6O-wq-yrc">
<barButtonItem key="leftBarButtonItem" image="settings_icon" id="QSE-cg-V2m">
<userDefinedRuntimeAttributes>
@ -288,15 +288,12 @@
<connections>
<outlet property="searchBarButtonIem" destination="pud-Fh-Usd" id="DiH-Re-nZE"/>
<outlet property="settingsBarButtonItem" destination="QSE-cg-V2m" id="N6b-Ju-w9g"/>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showRoomDetails" id="tk8-vF-K4T"/>
<segue destination="ZlD-EU-ncw" kind="presentation" identifier="showAuth" modalPresentationStyle="fullScreen" id="fpJ-zM-Qpo"/>
<segue destination="pBa-To-3YT" kind="relationship" relationship="viewControllers" id="Sfs-mh-GOz"/>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showContactDetails" id="p4M-yR-DLA"/>
<segue destination="HnD-LA-psC" kind="relationship" relationship="viewControllers" id="zON-Qp-EVq"/>
<segue destination="IGB-jr-yFz" kind="relationship" relationship="viewControllers" id="CeF-yL-aO0"/>
<segue destination="HPQ-zg-lZR" kind="relationship" relationship="viewControllers" id="y12-eg-vhO"/>
<segue destination="SLx-Wj-p7E" kind="relationship" relationship="viewControllers" id="MLn-VT-30W"/>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showGroupDetails" id="fIR-e3-ssz"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="k1S-FF-3Zu" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -342,7 +339,7 @@
<!--RecentsSplitVC-->
<scene sceneID="Nki-YV-4Qg">
<objects>
<splitViewController title="RecentsSplitVC" id="H1p-Uh-vWS" customClass="RiotSplitViewController" sceneMemberID="viewController">
<splitViewController storyboardIdentifier="RiotSplitViewController" title="RecentsSplitVC" id="H1p-Uh-vWS" customClass="RiotSplitViewController" sceneMemberID="viewController">
<toolbarItems/>
<navigationItem key="navigationItem" id="EB5-8V-irH"/>
<connections>
@ -422,10 +419,10 @@
</objects>
<point key="canvasLocation" x="-1375" y="-1121"/>
</scene>
<!--View Controller-->
<!--Placeholder Detail View Controller-->
<scene sceneID="2wP-Cu-Wca">
<objects>
<viewController storyboardIdentifier="EmptyDetailsViewControllerStoryboardId" id="Cpr-Tz-Az0" sceneMemberID="viewController">
<viewController storyboardIdentifier="EmptyDetailsViewControllerStoryboardId" id="Cpr-Tz-Az0" customClass="PlaceholderDetailViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="yS4-RU-AsN"/>
<viewControllerLayoutGuide type="bottom" id="VKG-pW-vMU"/>
@ -434,8 +431,12 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="6LG-qc-7jf">
<rect key="frame" x="127.5" y="283" width="120" height="101"/>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_logo" translatesAutoresizingMaskIntoConstraints="NO" id="6LG-qc-7jf">
<rect key="frame" x="127.5" y="273.5" width="120" height="120"/>
<constraints>
<constraint firstAttribute="width" secondItem="6LG-qc-7jf" secondAttribute="height" multiplier="1:1" id="4Qy-7P-CuE"/>
<constraint firstAttribute="width" constant="120" id="E2Y-gX-itg"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -445,6 +446,9 @@
</constraints>
</view>
<navigationItem key="navigationItem" id="c9s-dM-eEg"/>
<connections>
<outlet property="logoImageView" destination="6LG-qc-7jf" id="vLR-KE-0aH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pZm-ap-zdK" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
@ -590,15 +594,14 @@
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="Tfl-tq-LQp"/>
<segue reference="fIR-e3-ssz"/>
<segue reference="mhb-l9-pM3"/>
<segue reference="f5u-Y1-7nt"/>
<segue reference="vCz-dl-6xQ"/>
<segue reference="cUw-vU-gJq"/>
</inferredMetricsTieBreakers>
<resources>
<image name="integrations_icon" width="24" height="24"/>
<image name="logo" width="120" height="101"/>
<image name="launch_screen_logo" width="240" height="240"/>
<image name="search_icon" width="24" height="24"/>
<image name="settings_icon" width="24" height="24"/>
<image name="tab_favourites" width="24" height="24"/>

View file

@ -35,7 +35,6 @@ internal enum InfoPlist {
internal static let nsSiriUsageDescription: String = _document["NSSiriUsageDescription"]
internal static let uiBackgroundModes: [String] = _document["UIBackgroundModes"]
internal static let uiLaunchStoryboardName: String = _document["UILaunchStoryboardName"]
internal static let uiMainStoryboardFile: String = _document["UIMainStoryboardFile"]
internal static let uiRequiredDeviceCapabilities: [String] = _document["UIRequiredDeviceCapabilities"]
internal static let uiStatusBarHidden: Bool = _document["UIStatusBarHidden"]
internal static let uiStatusBarTintParameters: [String: Any] = _document["UIStatusBarTintParameters"]

View file

@ -115,6 +115,8 @@ NSString *const kThemeServiceDidChangeThemeNotification = @"kThemeServiceDidChan
// Observe "Invert Colours" settings changes (available since iOS 11)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accessibilityInvertColorsStatusDidChange) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil];
}
[self reEvaluateTheme];
}
return self;
}

View file

@ -0,0 +1,109 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
import Intents
/// The AppCoordinator is responsible of screen navigation and data injection at root application level. It decides if authentication or home screen should be shown and inject data needed for these flows, it changes the navigation stack on deep link, displays global warning.
/// This class should avoid to contain too many data management code not related to screen navigation logic. For example `MXSession` or push notification management should be handled in dedicated classes and report only navigation changes to the AppCoordinator.
final class AppCoordinator: NSObject, AppCoordinatorType {
// MARK: - Constants
// MARK: - Properties
// MARK: Private
private let rootRouter: RootRouterType
// swiftlint:disable weak_delegate
private let legacyAppDelegate: LegacyAppDelegate = AppDelegate.theDelegate()
// swiftlint:enable weak_delegate
private weak var splitViewCoordinator: SplitViewCoordinatorType?
// TODO: Use a dedicated class to handle Matrix sessions
/// Main user Matrix session
private var mainSession: MXSession? {
return MXKAccountManager.shared().activeAccounts.first?.mxSession
}
// MARK: Public
var childCoordinators: [Coordinator] = []
// MARK: - Setup
init(router: RootRouterType) {
self.rootRouter = router
}
// MARK: - Public methods
func start() {
self.showSplitView(session: self.mainSession)
}
// MARK: - Private methods
private func showAuthentication() {
// TODO: Implement
}
private func showLoading() {
// TODO: Implement
}
private func showPinCode() {
// TODO: Implement
}
private func showSplitView(session: MXSession?) {
let splitViewCoordinator = SplitViewCoordinator(router: self.rootRouter, session: session)
splitViewCoordinator.delegate = self
splitViewCoordinator.start()
self.add(childCoordinator: splitViewCoordinator)
self.splitViewCoordinator = splitViewCoordinator
}
private func checkAppVersion() {
// TODO: Implement
}
private func showError(_ error: Error) {
// FIXME: Present an error on coordinator.toPresentable()
self.legacyAppDelegate.showError(asAlert: error)
}
}
// MARK: - LegacyAppDelegateDelegate
extension AppCoordinator: LegacyAppDelegateDelegate {
func legacyAppDelegate(_ legacyAppDelegate: LegacyAppDelegate!, wantsToPopToHomeViewControllerAnimated animated: Bool, completion: (() -> Void)!) {
self.splitViewCoordinator?.popToHome(animated: animated, completion: completion)
}
func legacyAppDelegateRestoreEmptyDetailsViewController(_ legacyAppDelegate: LegacyAppDelegate!) {
self.splitViewCoordinator?.restorePlaceholderDetails()
}
}
// MARK: - SplitViewCoordinatorDelegate
extension AppCoordinator: SplitViewCoordinatorDelegate {
func splitViewCoordinatorDidCompleteAuthentication(_ coordinator: SplitViewCoordinatorType) {
self.legacyAppDelegate.authenticationDidComplete()
}
}

View file

@ -0,0 +1,21 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// `AppCoordinatorType` is a protocol describing a Coordinator that handles application navigation flow.
protocol AppCoordinatorType: Coordinator {
}

View file

@ -21,9 +21,12 @@ import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Properties
// MARK: Private
private var appCoordinator: AppCoordinatorType!
private var rootRouter: RootRouterType!
private var legacyAppDelegate: LegacyAppDelegate {
return AppDelegate.theDelegate()
}
@ -54,6 +57,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Setup window
let window = UIWindow(frame: UIScreen.main.bounds)
self.window = window
// Create AppCoordinator
self.rootRouter = RootRouter(window: window)
let appCoordinator = AppCoordinator(router: self.rootRouter)
appCoordinator.start()
self.legacyAppDelegate.delegate = appCoordinator
self.appCoordinator = appCoordinator
// Call legacy AppDelegate
self.legacyAppDelegate.window = window
self.legacyAppDelegate.application(application, didFinishLaunchingWithOptions: launchOptions)

View file

@ -28,7 +28,7 @@
#import "UniversalLink.h"
@protocol Configurable;
@protocol LegacyAppDelegateDelegate;
#pragma mark - Notifications
/**
@ -56,6 +56,8 @@ extern NSString *const AppDelegateUniversalLinkDidChangeNotification;
void (^_completionHandler)(UIBackgroundFetchResult);
}
@property (weak, nonatomic) id<LegacyAppDelegateDelegate> delegate;
/**
Application main view controller
*/
@ -176,6 +178,8 @@ extern NSString *const AppDelegateUniversalLinkDidChangeNotification;
- (void)configureCallManagerIfRequiredForSession:(MXSession *)mxSession;
- (void)authenticationDidComplete;
#pragma mark - Matrix Accounts handling
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection;
@ -234,3 +238,10 @@ extern NSString *const AppDelegateUniversalLinkDidChangeNotification;
- (void)checkAppVersion;
@end
@protocol LegacyAppDelegateDelegate <NSObject>
- (void)legacyAppDelegate:(LegacyAppDelegate*)legacyAppDelegate wantsToPopToHomeViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion;
- (void)legacyAppDelegateRestoreEmptyDetailsViewController:(LegacyAppDelegate*)legacyAppDelegate;
@end

View file

@ -89,7 +89,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUniversalLinkDidChangeNotification";
@interface LegacyAppDelegate () <GDPRConsentViewControllerDelegate, KeyVerificationCoordinatorBridgePresenterDelegate, ServiceTermsModalCoordinatorBridgePresenterDelegate, PushNotificationServiceDelegate, SetPinCoordinatorBridgePresenterDelegate, MasterTabBarControllerDelegate>
@interface LegacyAppDelegate () <GDPRConsentViewControllerDelegate, KeyVerificationCoordinatorBridgePresenterDelegate, ServiceTermsModalCoordinatorBridgePresenterDelegate, PushNotificationServiceDelegate, SetPinCoordinatorBridgePresenterDelegate>
{
/**
Reachability observer
@ -176,11 +176,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
*/
BOOL isErrorNotificationSuspended;
/**
Completion block called when [self popToHomeViewControllerAnimated:] has been
completed.
*/
void (^popToHomeViewControllerCompletion)(void);
/**
The listeners to call events.
@ -375,27 +370,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}
}
- (UINavigationController*)secondaryNavigationController
{
UIViewController* rootViewController = self.window.rootViewController;
if ([rootViewController isKindOfClass:[UISplitViewController class]])
{
UISplitViewController *splitViewController = (UISplitViewController *)rootViewController;
if (splitViewController.viewControllers.count == 2)
{
UIViewController *secondViewController = [splitViewController.viewControllers lastObject];
if ([secondViewController isKindOfClass:[UINavigationController class]])
{
return (UINavigationController*)secondViewController;
}
}
}
return nil;
}
#pragma mark - UIApplicationDelegate
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
@ -466,28 +440,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// To simplify navigation into the app, we retrieve here the main navigation controller and the tab bar controller.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
splitViewController.delegate = self;
_masterNavigationController = splitViewController.viewControllers[0];
_masterTabBarController = _masterNavigationController.viewControllers.firstObject;
// Force the background color of the fake view controller displayed when there is no details.
UINavigationController *secondNavController = self.secondaryNavigationController;
if (secondNavController)
{
[ThemeService.shared.theme applyStyleOnNavigationBar:secondNavController.navigationBar];
secondNavController.topViewController.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
}
// on IOS 8 iPad devices, force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else, the user cannot select a room
// shouldHideViewController delegate method is also implemented
if ([(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"])
{
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
}
// Sanity check
NSAssert(_masterTabBarController, @"Something wrong in Main.storyboard");
@ -938,32 +894,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)restoreEmptyDetailsViewController
{
UIViewController* rootViewController = self.window.rootViewController;
if ([rootViewController isKindOfClass:[UISplitViewController class]])
{
UISplitViewController *splitViewController = (UISplitViewController *)rootViewController;
// Be sure that the primary is then visible too.
if (splitViewController.displayMode == UISplitViewControllerDisplayModePrimaryHidden)
{
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
}
if (splitViewController.viewControllers.count == 2)
{
UIViewController *mainViewController = splitViewController.viewControllers[0];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UIViewController *emptyDetailsViewController = [storyboard instantiateViewControllerWithIdentifier:@"EmptyDetailsViewControllerStoryboardId"];
emptyDetailsViewController.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
splitViewController.viewControllers = @[mainViewController, emptyDetailsViewController];
}
}
// Release the current selected item (room/contact/group...).
[_masterTabBarController releaseSelectedItem];
[self.delegate legacyAppDelegateRestoreEmptyDetailsViewController:self];
}
- (UIAlertController*)showErrorAsAlert:(NSError*)error
@ -1136,61 +1067,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)popToHomeViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
{
UINavigationController *secondNavController = self.secondaryNavigationController;
if (secondNavController)
{
[secondNavController popToRootViewControllerAnimated:animated];
}
// Force back to the main screen if this is not the one that is displayed
if (_masterTabBarController && _masterTabBarController != _masterNavigationController.visibleViewController)
{
// Listen to the masterNavigationController changes
// We need to be sure that masterTabBarController is back to the screen
popToHomeViewControllerCompletion = completion;
_masterNavigationController.delegate = self;
[_masterNavigationController popToViewController:_masterTabBarController animated:animated];
}
else
{
// Select the Home tab
_masterTabBarController.selectedIndex = TABBAR_HOME_INDEX;
if (completion)
{
completion();
}
}
}
#pragma mark - UINavigationController delegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (viewController == _masterTabBarController)
{
_masterNavigationController.delegate = nil;
// For unknown reason, the navigation bar is not restored correctly by [popToViewController:animated:]
// when a ViewController has hidden it (see MXKAttachmentsViewController).
// Patch: restore navigation bar by default here.
_masterNavigationController.navigationBarHidden = NO;
// Release the current selected item (room/contact/...).
[_masterTabBarController releaseSelectedItem];
if (popToHomeViewControllerCompletion)
{
void (^popToHomeViewControllerCompletion2)(void) = popToHomeViewControllerCompletion;
popToHomeViewControllerCompletion = nil;
// Dispatch the completion in order to let navigation stack refresh itself.
dispatch_async(dispatch_get_main_queue(), ^{
popToHomeViewControllerCompletion2();
});
}
}
[self.delegate legacyAppDelegate:self wantsToPopToHomeViewControllerAnimated:animated completion:completion];
}
#pragma mark - Crash handling
@ -2448,8 +2325,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
NSLog(@"[AppDelegate] handleLaunchAnimation: Authentication still in progress");
// Wait for the return of masterTabBarControllerDidCompleteAuthentication
isLaunching = YES;
_masterTabBarController.masterVCDelegate = self;
isLaunching = YES;
}
else
{
@ -2621,6 +2497,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}
}
- (void)authenticationDidComplete
{
[self handleLaunchAnimation];
}
#pragma mark -
/**
@ -3613,50 +3494,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
[rootController.view setNeedsLayout];
}
#pragma mark - SplitViewController delegate
- (nullable UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController
{
// Return the top view controller of the master navigation controller, if it is a navigation controller itself.
UIViewController *topViewController = _masterNavigationController.topViewController;
if ([topViewController isKindOfClass:UINavigationController.class])
{
return topViewController;
}
// Else return the default empty details view controller from the storyboard.
// Be sure that the primary is then visible too.
if (splitViewController.displayMode == UISplitViewControllerDisplayModePrimaryHidden)
{
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
}
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UIViewController *emptyDetailsViewController = [storyboard instantiateViewControllerWithIdentifier:@"EmptyDetailsViewControllerStoryboardId"];
emptyDetailsViewController.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
return emptyDetailsViewController;
}
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController
{
if (!self.masterTabBarController.currentRoomViewController && !self.masterTabBarController.currentContactDetailViewController && !self.masterTabBarController.currentGroupDetailViewController)
{
// Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return YES;
}
else
{
return NO;
}
}
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
// oniPad devices, force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else, the user cannot select a room
return NO;
}
#pragma mark - Status Bar Tap handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
@ -4710,11 +4547,4 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
[self logoutWithConfirmation:NO completion:nil];
}
#pragma mark - MasterTabBarControllerDelegate
- (void)masterTabBarControllerDidCompleteAuthentication:(MasterTabBarController *)masterTabBarController
{
[self handleLaunchAnimation];
}
@end

View file

@ -96,7 +96,7 @@ class PlaceholderedTextView: UITextView {
NotificationCenter.default.addObserver(self, selector: #selector(textViewTextChanged(_:)), name: UITextView.textDidChangeNotification, object: self)
kvoLineFragmentPadding = observe(\.textContainer.lineFragmentPadding, options: [.new]) { [weak self] (_, change) in
guard let self = self else { return }
let newValue = change.newValue ?? 0
let newValue = change.newValue ?? 0
self.placeholderTextView.textContainer.lineFragmentPadding = newValue
}
}

View file

@ -29,7 +29,7 @@ final class KeyVerificationCoordinator: KeyVerificationCoordinatorType {
private let session: MXSession
private let verificationFlow: KeyVerificationFlow
private let verificationKind: KeyVerificationKind
private var completeSecurityCoordinator: KeyVerificationSelfVerifyWaitCoordinatorType?
private weak var completeSecurityCoordinator: KeyVerificationSelfVerifyWaitCoordinatorType?
private var otherUserId: String {

View file

@ -0,0 +1,81 @@
//
// Copyright 2020 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
/// Used as a placeholder for UISplitViewController detail view controller
final class PlaceholderDetailViewController: UIViewController, Themable {
// MARK: - Constants
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var logoImageView: UIImageView!
// MARK: Private
private var theme: Theme!
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.setupViews()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Public
// TODO: Extract Storyboard and use SwiftGen
class func instantiate() -> PlaceholderDetailViewController {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let emptyDetailsViewController = storyboard.instantiateViewController(withIdentifier: "EmptyDetailsViewControllerStoryboardId") as? PlaceholderDetailViewController else {
fatalError("[PlaceholderDetailViewController] Fail to load view controller from storyboard")
}
emptyDetailsViewController.theme = ThemeService.shared().theme
return emptyDetailsViewController
}
func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.backgroundColor
self.logoImageView.tintColor = theme.tintColor
}
// MARK: - Private
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
self.logoImageView.image = Asset.Images.launchScreenLogo.image
}
}

View file

@ -0,0 +1,172 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
// MARK: - Properties
// MARK: Private
private let rootRouter: RootRouterType
private var session: MXSession?
private let splitViewController: UISplitViewController
private weak var masterPresentable: SplitViewMasterPresentable?
private weak var detailNavigationController: UINavigationController?
private weak var tabBarCoordinator: TabBarCoordinatorType?
// MARK: Public
var childCoordinators: [Coordinator] = []
weak var delegate: SplitViewCoordinatorDelegate?
// MARK: - Setup
// TODO: Improve sessions injection
// at the moment the session is not used, see TabBarCoordinator `init`.
init(router: RootRouterType, session: MXSession?) {
self.rootRouter = router
self.session = session
let splitViewController = UISplitViewController()
splitViewController.preferredDisplayMode = .allVisible
self.splitViewController = splitViewController
}
// MARK: - Public methods
func start() {
self.splitViewController.delegate = self
let tabBarCoordinator = self.createTabBarCoordinator()
tabBarCoordinator.delegate = self
tabBarCoordinator.splitViewMasterPresentableDelegate = self
tabBarCoordinator.start()
let detailNavigationController = self.createDetailNavigationController()
self.splitViewController.viewControllers = [tabBarCoordinator.toPresentable(), detailNavigationController]
self.add(childCoordinator: tabBarCoordinator)
self.tabBarCoordinator = tabBarCoordinator
self.masterPresentable = tabBarCoordinator
self.detailNavigationController = detailNavigationController
self.rootRouter.setRootModule(self.splitViewController)
}
func update(with session: MXSession) {
self.session = session
}
func toPresentable() -> UIViewController {
return self.splitViewController
}
// TODO: Do not expose publicly this method
func restorePlaceholderDetails() {
// Be sure that the primary is then visible too.
if splitViewController.displayMode == .primaryHidden {
splitViewController.preferredDisplayMode = .allVisible
}
if splitViewController.viewControllers.count == 2 {
let mainViewController = splitViewController.viewControllers[0]
let emptyDetailsViewController = self.createPlaceholderDetailsViewController()
splitViewController.viewControllers = [mainViewController, emptyDetailsViewController]
}
// Release the current selected item (room/contact/group...).
self.tabBarCoordinator?.releaseSelectedItems()
}
func popToHome(animated: Bool, completion: (() -> Void)?) {
if let secondNavController = self.detailNavigationController {
secondNavController.popToRootViewController(animated: animated)
}
// Force back to the main screen if this is not the one that is displayed
self.tabBarCoordinator?.popToHome(animated: animated, completion: completion)
}
// MARK: - Private methods
private func createPlaceholderDetailsViewController() -> UIViewController {
return PlaceholderDetailViewController.instantiate()
}
private func createTabBarCoordinator() -> TabBarCoordinator {
let tabBarCoordinator = TabBarCoordinator(session: self.session)
tabBarCoordinator.delegate = self
return tabBarCoordinator
}
private func createDetailNavigationController() -> UINavigationController {
let placeholderDetailViewController = self.createPlaceholderDetailsViewController()
let detailNavigationController = RiotNavigationController(rootViewController: placeholderDetailViewController)
return detailNavigationController
}
}
// MARK: - UISplitViewControllerDelegate
extension SplitViewCoordinator: UISplitViewControllerDelegate {
func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
if let detailViewController = self.masterPresentable?.secondViewControllerWhenSeparatedFromPrimary() {
return detailViewController
}
// Else return the default empty details view controller from the storyboard.
// Be sure that the primary is then visible too.
if splitViewController.displayMode == .primaryHidden {
splitViewController.preferredDisplayMode = .allVisible
}
return self.createPlaceholderDetailsViewController()
}
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
return self.masterPresentable?.collapseDetailViewController ?? false
}
}
/// MARK: - UINavigationControllerDelegate
extension SplitViewCoordinator: TabBarCoordinatorDelegate {
func tabBarCoordinatorDidCompleteAuthentication(_ coordinator: TabBarCoordinatorType) {
self.delegate?.splitViewCoordinatorDidCompleteAuthentication(self)
}
}
/// MARK: - SplitViewMasterPresentableDelegate
extension SplitViewCoordinator: SplitViewMasterPresentableDelegate {
func splitViewMasterPresentable(_ presentable: Presentable, wantsToDisplay detailPresentable: Presentable) {
guard let detailNavigationController = self.detailNavigationController else {
return
}
detailNavigationController.viewControllers = [detailPresentable.toPresentable()]
self.splitViewController.showDetailViewController(detailNavigationController, sender: nil)
}
}

View file

@ -0,0 +1,33 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol SplitViewCoordinatorDelegate: class {
// TODO: Remove this method, authentication should not be handled by SplitViewCoordinator
func splitViewCoordinatorDidCompleteAuthentication(_ coordinator: SplitViewCoordinatorType)
}
/// `SplitViewCoordinatorType` is a protocol describing a Coordinator that handles split view navigation flow.
protocol SplitViewCoordinatorType: Coordinator, Presentable {
var delegate: SplitViewCoordinatorDelegate? { get }
func popToHome(animated: Bool, completion: (() -> Void)?)
// TODO: Do not expose publicly this method
func restorePlaceholderDetails()
}

View file

@ -0,0 +1,33 @@
//
// Copyright 2020 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
protocol SplitViewMasterPresentableDelegate: class {
func splitViewMasterPresentable(_ presentable: Presentable, wantsToDisplay detailPresentable: Presentable)
}
/// Protocol used by the master view presentable of a UISplitViewController
protocol SplitViewMasterPresentable: class, Presentable {
var splitViewMasterPresentableDelegate: SplitViewMasterPresentableDelegate? { get set }
/// Indicate true if the detail can be collapsed
var collapseDetailViewController: Bool { get }
/// Return the detail view controller to display when the detail is separated from the master view controller
func secondViewControllerWhenSeparatedFromPrimary() -> UIViewController?
}

View file

@ -42,6 +42,9 @@
@interface MasterTabBarController : UITabBarController
// UITabBarController already have a `delegate` property
@property (weak, nonatomic) id<MasterTabBarControllerDelegate> masterTabBarDelegate;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *settingsBarButtonItem;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *searchBarButtonIem;
@ -141,7 +144,6 @@
*/
- (void)refreshTabBarBadges;
@property (nonatomic, weak) id<MasterTabBarControllerDelegate> masterVCDelegate;
// Reference to the current auth VC. It is not nil only when the auth screen is displayed.
@property (nonatomic, readonly) AuthenticationViewController *authViewController;
@ -178,5 +180,6 @@
@protocol MasterTabBarControllerDelegate <NSObject>
- (void)masterTabBarControllerDidCompleteAuthentication:(MasterTabBarController *)masterTabBarController;
- (void)masterTabBarController:(MasterTabBarController*)masterTabBarController wantsToDisplayDetailViewController:(UIViewController*)detailViewController;
@end;
@end

View file

@ -477,6 +477,60 @@
}
}
- (void)showRoomDetails
{
[self releaseCurrentDetailsViewController];
if (_selectedRoomPreviewData)
{
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
[self.masterTabBarDelegate masterTabBarController:self wantsToDisplayDetailViewController:_currentRoomViewController];
[_currentRoomViewController displayRoomPreview:_selectedRoomPreviewData];
_selectedRoomPreviewData = nil;
[self setupLeftBarButtonItem];
}
else
{
MXWeakify(self);
void (^openRoomDataSource)(MXKRoomDataSource *roomDataSource) = ^(MXKRoomDataSource *roomDataSource) {
MXStrongifyAndReturnIfNil(self);
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
self->_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
[self.masterTabBarDelegate masterTabBarController:self wantsToDisplayDetailViewController:self.currentRoomViewController];
[self.currentRoomViewController displayRoom:roomDataSource];
[self setupLeftBarButtonItem];
};
if (_selectedRoomDataSource)
{
// If the room data source is already loaded, display it
openRoomDataSource(_selectedRoomDataSource);
_selectedRoomDataSource = nil;
}
else
{
// Else, load it. The user may see the EmptyDetailsViewControllerStoryboardId
// screen in this case
[self dataSourceOfRoomToDisplay:^(MXKRoomDataSource *roomDataSource) {
openRoomDataSource(roomDataSource);
}];
}
}
}
- (void)selectRoomWithId:(NSString*)roomId andEventId:(NSString*)eventId inMatrixSession:(MXSession*)matrixSession
{
[self selectRoomWithId:roomId andEventId:eventId inMatrixSession:matrixSession completion:nil];
@ -509,7 +563,7 @@
self->_selectedRoomDataSource = roomDataSource;
[self performSegueWithIdentifier:@"showRoomDetails" sender:self];
[self showRoomDetails];
if (completion)
{
@ -533,14 +587,28 @@
_selectedRoomId = roomPreviewData.roomId;
_selectedRoomSession = roomPreviewData.mxSession;
[self performSegueWithIdentifier:@"showRoomDetails" sender:self];
[self showRoomDetails];
}
- (void)selectContact:(MXKContact*)contact
{
_selectedContact = contact;
[self performSegueWithIdentifier:@"showContactDetails" sender:self];
[self showContactDetails];
}
- (void)showContactDetails
{
[self releaseCurrentDetailsViewController];
// Replace the rootviewcontroller with a contact details view controller
_currentContactDetailViewController = [ContactDetailsViewController contactDetailsViewController];
_currentContactDetailViewController.enableVoipCall = NO;
_currentContactDetailViewController.contact = _selectedContact;
[self.masterTabBarDelegate masterTabBarController:self wantsToDisplayDetailViewController:_currentContactDetailViewController];
[self setupLeftBarButtonItem];
}
- (void)selectGroup:(MXGroup*)group inMatrixSession:(MXSession*)matrixSession
@ -548,7 +616,20 @@
_selectedGroup = group;
_selectedGroupSession = matrixSession;
[self performSegueWithIdentifier:@"showGroupDetails" sender:self];
[self showGroupDetails];
}
- (void)showGroupDetails
{
[self releaseCurrentDetailsViewController];
// Replace the rootviewcontroller with a group details view controller
_currentGroupDetailViewController = [GroupDetailsViewController groupDetailsViewController];
[_currentGroupDetailViewController setGroup:_selectedGroup withMatrixSession:_selectedGroupSession];
[self.masterTabBarDelegate masterTabBarController:self wantsToDisplayDetailViewController:_currentGroupDetailViewController];
[self setupLeftBarButtonItem];
}
- (void)releaseSelectedItem
@ -608,137 +689,55 @@
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showRoomDetails"] || [[segue identifier] isEqualToString:@"showContactDetails"] || [[segue identifier] isEqualToString:@"showGroupDetails"])
// Keep ref on destinationViewController
[childViewControllers addObject:segue.destinationViewController];
if ([[segue identifier] isEqualToString:@"showAuth"])
{
UINavigationController *navigationController = [segue destinationViewController];
// Keep ref on the authentification view controller while it is displayed
// ie until we get the notification about a new account
_authViewController = segue.destinationViewController;
isAuthViewControllerPreparing = NO;
[self releaseCurrentDetailsViewController];
// Listen to the end of the authentication flow
_authViewController.authVCDelegate = self;
if ([[segue identifier] isEqualToString:@"showRoomDetails"])
{
if (_selectedRoomPreviewData)
{
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
navigationController.viewControllers = @[_currentRoomViewController];
[_currentRoomViewController displayRoomPreview:_selectedRoomPreviewData];
_selectedRoomPreviewData = nil;
[self setupLeftBarButtonItem];
}
else
{
MXWeakify(self);
void (^openRoomDataSource)(MXKRoomDataSource *roomDataSource) = ^(MXKRoomDataSource *roomDataSource) {
MXStrongifyAndReturnIfNil(self);
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
self->_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
navigationController.viewControllers = @[self.currentRoomViewController];
[self.currentRoomViewController displayRoom:roomDataSource];
[self setupLeftBarButtonItem];
};
if (_selectedRoomDataSource)
{
// If the room data source is already loaded, display it
openRoomDataSource(_selectedRoomDataSource);
_selectedRoomDataSource = nil;
}
else
{
// Else, load it. The user may see the EmptyDetailsViewControllerStoryboardId
// screen in this case
[self dataSourceOfRoomToDisplay:^(MXKRoomDataSource *roomDataSource) {
openRoomDataSource(roomDataSource);
}];
}
}
}
else if ([[segue identifier] isEqualToString:@"showContactDetails"])
{
// Replace the rootviewcontroller with a contact details view controller
_currentContactDetailViewController = [ContactDetailsViewController contactDetailsViewController];
_currentContactDetailViewController.enableVoipCall = NO;
_currentContactDetailViewController.contact = _selectedContact;
authViewControllerObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
navigationController.viewControllers = @[_currentContactDetailViewController];
[self setupLeftBarButtonItem];
}
else
{
// Replace the rootviewcontroller with a group details view controller
_currentGroupDetailViewController = [GroupDetailsViewController groupDetailsViewController];
[_currentGroupDetailViewController setGroup:_selectedGroup withMatrixSession:_selectedGroupSession];
_authViewController = nil;
navigationController.viewControllers = @[_currentGroupDetailViewController];
[self setupLeftBarButtonItem];
[[NSNotificationCenter defaultCenter] removeObserver:authViewControllerObserver];
authViewControllerObserver = nil;
}];
authViewRemovedAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// The user has cleared data for their soft logged out account
_authViewController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:authViewRemovedAccountObserver];
authViewRemovedAccountObserver = nil;
}];
// Forward parameters if any
if (authViewControllerRegistrationParameters)
{
_authViewController.externalRegistrationParameters = authViewControllerRegistrationParameters;
authViewControllerRegistrationParameters = nil;
}
if (softLogoutCredentials)
{
_authViewController.softLogoutCredentials = softLogoutCredentials;
softLogoutCredentials = nil;
}
}
else
else if ([[segue identifier] isEqualToString:@"showUnifiedSearch"])
{
// Keep ref on destinationViewController
[childViewControllers addObject:segue.destinationViewController];
unifiedSearchViewController= segue.destinationViewController;
if ([[segue identifier] isEqualToString:@"showAuth"])
for (MXSession *session in mxSessionArray)
{
// Keep ref on the authentification view controller while it is displayed
// ie until we get the notification about a new account
_authViewController = segue.destinationViewController;
isAuthViewControllerPreparing = NO;
// Listen to the end of the authentication flow
_authViewController.authVCDelegate = self;
authViewControllerObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
_authViewController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:authViewControllerObserver];
authViewControllerObserver = nil;
}];
authViewRemovedAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// The user has cleared data for their soft logged out account
_authViewController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:authViewRemovedAccountObserver];
authViewRemovedAccountObserver = nil;
}];
// Forward parameters if any
if (authViewControllerRegistrationParameters)
{
_authViewController.externalRegistrationParameters = authViewControllerRegistrationParameters;
authViewControllerRegistrationParameters = nil;
}
if (softLogoutCredentials)
{
_authViewController.softLogoutCredentials = softLogoutCredentials;
softLogoutCredentials = nil;
}
}
else if ([[segue identifier] isEqualToString:@"showUnifiedSearch"])
{
unifiedSearchViewController= segue.destinationViewController;
for (MXSession *session in mxSessionArray)
{
[unifiedSearchViewController addMatrixSession:session];
}
[unifiedSearchViewController addMatrixSession:session];
}
}
@ -1175,10 +1174,7 @@
- (void)authenticationViewControllerDidDismiss:(AuthenticationViewController *)authenticationViewController
{
_authenticationInProgress = NO;
if (self.masterVCDelegate)
{
[self.masterVCDelegate masterTabBarControllerDidCompleteAuthentication:self];
}
[self.masterTabBarDelegate masterTabBarControllerDidCompleteAuthentication:self];
}
@end

View file

@ -0,0 +1,192 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh TabBar TabBar
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
@objcMembers
final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
// MARK: - Properties
// MARK: Private
private var session: MXSession?
/// Completion called when `popToHomeAnimated:` has been completed.
private var popToHomeViewControllerCompletion: (() -> Void)?
// TODO: Move MasterTabBarController navigation code here
// and if possible use a simple: `private let tabBarController: UITabBarController`
private var masterTabBarController: MasterTabBarController!
// TODO: Embed UINavigationController in each tab like recommended by Apple and remove these properties. UITabBarViewController shoud not be embed in a UINavigationController (https://github.com/vector-im/riot-ios/issues/3086).
private let navigationRouter: NavigationRouterType
private let masterNavigationController: UINavigationController
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: TabBarCoordinatorDelegate?
weak var splitViewMasterPresentableDelegate: SplitViewMasterPresentableDelegate?
// MARK: - Setup
// TODO: Improve sessions injection
// at the moment Matrix session is injected to MasterTabBarController via LegacyAppDelegate
init(session: MXSession?) {
let masterNavigationController = RiotNavigationController()
self.navigationRouter = NavigationRouter(navigationController: masterNavigationController)
self.masterNavigationController = masterNavigationController
self.session = session
}
// MARK: - Public methods
func start() {
let masterTabBarController = self.createMasterTabBarController()
masterTabBarController.masterTabBarDelegate = self
self.masterTabBarController = masterTabBarController
self.navigationRouter.setRootModule(masterTabBarController)
}
func toPresentable() -> UIViewController {
return self.navigationRouter.toPresentable()
}
func releaseSelectedItems() {
self.masterTabBarController.releaseSelectedItem()
}
func popToHome(animated: Bool, completion: (() -> Void)?) {
// Force back to the main screen if this is not the one that is displayed
if masterTabBarController != masterNavigationController.visibleViewController {
// Listen to the masterNavigationController changes
// We need to be sure that masterTabBarController is back to the screen
popToHomeViewControllerCompletion = completion
masterNavigationController.delegate = self
masterNavigationController.popToViewController(masterTabBarController, animated: animated)
} else {
// Select the Home tab
masterTabBarController.selectedIndex = Int(TABBAR_HOME_INDEX)
completion?()
}
}
// MARK: - SplitViewMasterPresentable
var collapseDetailViewController: Bool {
if (masterTabBarController.currentRoomViewController == nil) && (masterTabBarController.currentContactDetailViewController == nil) && (masterTabBarController.currentGroupDetailViewController == nil) {
// Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return true
} else {
return false
}
}
func secondViewControllerWhenSeparatedFromPrimary() -> UIViewController? {
// Return the top view controller of the master navigation controller, if it is a navigation controller itself.
if let topViewController = masterNavigationController.topViewController as? UINavigationController {
// Keep the detail scene
return topViewController
}
return nil
}
// MARK: - Private methods
private func createMasterTabBarController() -> MasterTabBarController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let masterTabBarController = storyboard.instantiateViewController(withIdentifier: "MasterTabBarController") as? MasterTabBarController else {
fatalError("[TabBarCoordinator] Can't load MasterTabBarController")
}
return masterTabBarController
}
// MARK: Navigation
// FIXME: Should be displayed per tab.
private func showSettings() {
// TODO: Implement
}
// FIXME: Should be displayed per tab.
private func showUnifiedSearch() {
// TODO: Implement
}
// FIXME: Should be displayed from a tab.
private func showContactDetails() {
// TODO: Implement
}
// FIXME: Should be displayed from a tab.
private func showRoomDetails() {
// TODO: Implement
}
// FIXME: Should be displayed from a tab.
private func showGroupDetails() {
// TODO: Implement
}
}
// MARK: - UINavigationControllerDelegate
extension TabBarCoordinator: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if viewController == masterTabBarController {
masterNavigationController.delegate = nil
// For unknown reason, the navigation bar is not restored correctly by [popToViewController:animated:]
// when a ViewController has hidden it (see MXKAttachmentsViewController).
// Patch: restore navigation bar by default here.
masterNavigationController.isNavigationBarHidden = false
// Release the current selected item (room/contact/...).
masterTabBarController.releaseSelectedItem()
if let popToHomeViewControllerCompletion = self.popToHomeViewControllerCompletion {
let popToHomeViewControllerCompletion2: (() -> Void)? = popToHomeViewControllerCompletion
self.popToHomeViewControllerCompletion = nil
DispatchQueue.main.async {
popToHomeViewControllerCompletion2?()
}
}
}
}
}
// MARK: - MasterTabBarControllerDelegate
extension TabBarCoordinator: MasterTabBarControllerDelegate {
func masterTabBarControllerDidCompleteAuthentication(_ masterTabBarController: MasterTabBarController!) {
self.delegate?.tabBarCoordinatorDidCompleteAuthentication(self)
}
func masterTabBarController(_ masterTabBarController: MasterTabBarController!, wantsToDisplayDetailViewController detailViewController: UIViewController!) {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToDisplay: detailViewController)
}
}

View file

@ -0,0 +1,36 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh TabBar TabBar
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol TabBarCoordinatorDelegate: class {
// TODO: Remove this method, authentication should not be handled by TabBarCoordinator
func tabBarCoordinatorDidCompleteAuthentication(_ coordinator: TabBarCoordinatorType)
}
/// `TabBarCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
protocol TabBarCoordinatorType: Coordinator, SplitViewMasterPresentable {
var delegate: TabBarCoordinatorDelegate? { get }
func popToHome(animated: Bool, completion: (() -> Void)?)
// TODO: Remove this method, this implementation detail should not be exposed
// Release the current selected item (room/contact/group...).
func releaseSelectedItems()
}

87
Riot/Routers/RootRouter.swift Executable file
View file

@ -0,0 +1,87 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// `RootRouter` is a concrete implementation of RootRouterType.
final class RootRouter: RootRouterType {
// MARK: - Constants
// `rootViewController` animation constants
private enum RootViewControllerUpdateAnimation {
static let duration: TimeInterval = 0.3
static let options: UIView.AnimationOptions = .transitionCrossDissolve
}
// MARK: - Properties
private var presentedModule: Presentable?
let window: UIWindow
/// The root view controller currently presented
var rootViewController: UIViewController? {
return self.window.rootViewController
}
// MARK: - Setup
init(window: UIWindow) {
self.window = window
}
// MARK: - Public methods
func setRootModule(_ module: Presentable) {
self.updateRootViewController(rootViewController: module.toPresentable(), animated: false, completion: nil)
self.window.makeKeyAndVisible()
}
func dismissRootModule(animated: Bool, completion: (() -> Void)?) {
self.updateRootViewController(rootViewController: nil, animated: animated, completion: completion)
}
func presentModule(_ module: Presentable, animated: Bool, completion: (() -> Void)?) {
let viewControllerPresenter = self.rootViewController?.presentedViewController ?? self.rootViewController
viewControllerPresenter?.present(module.toPresentable(), animated: animated, completion: completion)
self.presentedModule = module
}
func dismissModule(animated: Bool, completion: (() -> Void)?) {
self.presentedModule?.toPresentable().dismiss(animated: animated, completion: completion)
}
// MARK: - Private methods
private func updateRootViewController(rootViewController: UIViewController?, animated: Bool, completion: (() -> Void)?) {
if animated {
UIView.transition(with: window, duration: RootViewControllerUpdateAnimation.duration, options: RootViewControllerUpdateAnimation.options, animations: {
let oldState: Bool = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(false)
self.window.rootViewController = rootViewController
UIView.setAnimationsEnabled(oldState)
}, completion: { (finished: Bool) -> Void in
completion?()
})
} else {
self.window.rootViewController = rootViewController
completion?()
}
}
}

View file

@ -0,0 +1,49 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
/// Protocol describing a router that wraps the root navigation of the application.
/// Routers are used to be passed between coordinators. They handles only `physical` navigation.
protocol RootRouterType: class {
/// Update the root view controller
///
/// - Parameter module: The new root view controller to set
func setRootModule(_ module: Presentable)
/// Dismiss the root view controller
///
/// - Parameters:
/// - animated: Specify true to animate the transition.
/// - completion: The closure executed after the view controller is dismissed.
func dismissRootModule(animated: Bool, completion: (() -> Void)?)
/// Present modally a view controller on the root view controller
///
/// - Parameters:
/// - module: Specify true to animate the transition.
/// - animated: Specify true to animate the transition.
/// - completion: Animation completion.
func presentModule(_ module: Presentable, animated: Bool, completion: (() -> Void)?)
/// Dismiss modally presented view controller from root view controller
///
/// - Parameters:
/// - animated: Specify true to animate the transition.
/// - completion: Animation completion.
func dismissModule(animated: Bool, completion: (() -> Void)?)
}

View file

@ -55,8 +55,6 @@
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>

View file

@ -22,6 +22,7 @@ import Foundation
/// FlowTemplateCoordinatorBridgePresenter enables to start FlowTemplateCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class FlowTemplateCoordinatorBridgePresenter: NSObject {