From 282889059771a41a3f8ef6b1915fe0ab1351ecbc Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 2 Aug 2019 12:49:00 +0200 Subject: [PATCH 01/11] IM: Detect not signed terms error (cherry picked from commit 80877eabafc2184ef55c279d2a8f2db10d9735e3) --- Riot/Managers/Widgets/WidgetManager.h | 4 +++- Riot/Managers/Widgets/WidgetManager.m | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Riot/Managers/Widgets/WidgetManager.h b/Riot/Managers/Widgets/WidgetManager.h index a15928789..f8d027185 100644 --- a/Riot/Managers/Widgets/WidgetManager.h +++ b/Riot/Managers/Widgets/WidgetManager.h @@ -56,10 +56,12 @@ typedef enum : NSUInteger WidgetManagerErrorCodeNotEnoughPower, WidgetManagerErrorCodeCreationFailed, WidgetManagerErrorCodeNoIntegrationsServerConfigured, - WidgetManagerErrorCodeFailedToConnectToIntegrationsServer + WidgetManagerErrorCodeFailedToConnectToIntegrationsServer, + WidgetManagerErrorCodeTermsNotSigned } WidgetManagerErrorCode; +FOUNDATION_EXPORT NSString *const WidgetManagerErrorOpenIdTokenKey; /** The `WidgetManager` helps to handle modular widgets. diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index a6d2bdec1..2505f7bd2 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -513,6 +513,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; MXHTTPOperation *operation; NSString *userId = mxSession.myUser.userId; + NSLog(@"[WidgetManager] registerForScalarToken"); + WidgetManagerConfig *config = [self configForUser:userId]; if (!config.hasUrls) { @@ -539,6 +541,10 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; MXJSONModelSetString(scalarToken, JSONResponse[@"scalar_token"]) config.scalarToken = scalarToken; + // Validate it (this mostly checks to see if the IM needs us to agree to some terms) + // TODO + // return this._checkToken(tokenObject); + self->configs[userId] = config; [self saveConfigs]; @@ -622,7 +628,22 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; } else if (failure) { - failure(error); + MXError *mxError = [[MXError alloc] initWithNSError:error]; + if ([mxError.errcode isEqualToString:kMXErrCodeStringTermsNotSigned]) + { + NSLog(@"[WidgetManager] validateScalarToke. Error: Need to accept terms"); + NSError *termsNotSignedError = [NSError errorWithDomain:WidgetManagerErrorDomain + code:WidgetManagerErrorCodeTermsNotSigned + userInfo:@{ + NSLocalizedDescriptionKey:error.userInfo[NSLocalizedDescriptionKey] + }]; + + failure(termsNotSignedError); + } + else + { + failure(error); + } } }]; } From b0bd5df851fcccdabb22ff202dbd00c3d82928e7 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 9 Aug 2019 15:58:33 +0200 Subject: [PATCH 02/11] IM: Start flow for terms modal --- Riot.xcodeproj/project.pbxproj | 68 +++++++ Riot/Assets/en.lproj/Vector.strings | 4 + Riot/Generated/Storyboards.swift | 5 + Riot/Generated/Strings.swift | 8 + .../IntegrationManagerViewController.m | 77 +++++++- .../ServiceTermsModalScreenCoordinator.swift | 69 +++++++ ...rviceTermsModalScreenCoordinatorType.swift | 29 +++ .../ServiceTermsModalScreenViewAction.swift | 26 +++ ...eTermsModalScreenViewController.storyboard | 88 +++++++++ ...erviceTermsModalScreenViewController.swift | 175 ++++++++++++++++++ .../ServiceTermsModalScreenViewModel.swift | 121 ++++++++++++ ...ServiceTermsModalScreenViewModelType.swift | 39 ++++ .../ServiceTermsModalScreenViewState.swift | 27 +++ .../Modal/ServiceTermsModalCoordinator.swift | 80 ++++++++ ...TermsModalCoordinatorBridgePresenter.swift | 95 ++++++++++ .../ServiceTermsModalCoordinatorType.swift | 29 +++ 16 files changed, 934 insertions(+), 6 deletions(-) create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinator.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModel.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewState.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinator.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorBridgePresenter.swift create mode 100644 Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 814e0b268..fcd8e851b 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -95,6 +95,17 @@ 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; }; 32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */; }; 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */; }; + 32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */; }; + 32DB557622FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */; }; + 32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */; }; + 32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */; }; + 32DB557922FDADE50016329E /* ServiceTermsModalScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */; }; + 32DB557A22FDADE50016329E /* ServiceTermsModalScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */; }; + 32DB557B22FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */; }; + 32DB557C22FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */; }; + 32DB557D22FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */; }; + 32DB557E22FDADE50016329E /* ServiceTermsModalScreenViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */; }; + 32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */; }; 32F6B9692270623100BBA352 /* DeviceVerificationDataLoadingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9632270623000BBA352 /* DeviceVerificationDataLoadingCoordinator.swift */; }; 32F6B96A2270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9642270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift */; }; 32F6B96B2270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9652270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift */; }; @@ -701,6 +712,17 @@ 32D7159E2146CC6F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Vector.strings; sourceTree = ""; }; 32D7159F2146CC7F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 32D715A02146CC8800DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + 32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinatorType.swift; sourceTree = ""; }; + 32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinatorBridgePresenter.swift; sourceTree = ""; }; + 32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinator.swift; sourceTree = ""; }; + 32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewState.swift; sourceTree = ""; }; + 32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewModel.swift; sourceTree = ""; }; + 32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewController.swift; sourceTree = ""; }; + 32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ServiceTermsModalScreenViewController.storyboard; sourceTree = ""; }; + 32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewModelType.swift; sourceTree = ""; }; + 32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenCoordinatorType.swift; sourceTree = ""; }; + 32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewAction.swift; sourceTree = ""; }; + 32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenCoordinator.swift; sourceTree = ""; }; 32F6B9632270623000BBA352 /* DeviceVerificationDataLoadingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingCoordinator.swift; sourceTree = ""; }; 32F6B9642270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewState.swift; sourceTree = ""; }; 32F6B9652270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewAction.swift; sourceTree = ""; }; @@ -1689,6 +1711,40 @@ path = KeyBackup; sourceTree = ""; }; + 32DB556722FDADE50016329E /* ServiceTerms */ = { + isa = PBXGroup; + children = ( + 32DB556822FDADE50016329E /* Modal */, + ); + path = ServiceTerms; + sourceTree = ""; + }; + 32DB556822FDADE50016329E /* Modal */ = { + isa = PBXGroup; + children = ( + 32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */, + 32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */, + 32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */, + 32DB556C22FDADE50016329E /* Modal */, + ); + path = Modal; + sourceTree = ""; + }; + 32DB556C22FDADE50016329E /* Modal */ = { + isa = PBXGroup; + children = ( + 32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */, + 32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */, + 32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */, + 32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */, + 32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */, + 32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */, + 32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */, + 32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */, + ); + path = Modal; + sourceTree = ""; + }; 4220F60B660591FD80AF3428 /* Pods */ = { isa = PBXGroup; children = ( @@ -2178,6 +2234,7 @@ B1B5567620EE6C4C00210D55 /* Modules */ = { isa = PBXGroup; children = ( + 32DB556722FDADE50016329E /* ServiceTerms */, 3232AB94225730E100AD6A5C /* DeviceVerification */, B1B556EA20EE6C4C00210D55 /* Main */, B1B556CA20EE6C4C00210D55 /* TabBar */, @@ -3833,6 +3890,7 @@ B1B5574D20EE6C4D00210D55 /* MediaPickerViewController.xib in Resources */, B1B5575020EE6C4D00210D55 /* AuthenticationViewController.xib in Resources */, B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */, + 32DB557B22FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard in Resources */, B1B5574320EE6C4D00210D55 /* CallViewController.xib in Resources */, F083BDEA1E7009ED00A9B29C /* ringback.mp3 in Resources */, F083BDF21E7009ED00A9B29C /* GoogleService-Info.plist in Resources */, @@ -4152,6 +4210,7 @@ B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */, B1B9DEDE22E9D9890065E677 /* EmojiServiceType.swift in Sources */, 3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */, + 32DB557C22FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift in Sources */, B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, @@ -4227,6 +4286,7 @@ B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */, 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */, B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */, + 32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */, B1B558E120EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.m in Sources */, B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */, B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */, @@ -4247,6 +4307,7 @@ 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */, B1B9DEE822EB34EF0065E677 /* ReactionHistoryCoordinatorType.swift in Sources */, B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */, + 32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */, B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */, B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */, 3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */, @@ -4254,6 +4315,8 @@ 3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */, F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */, B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */, + 32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */, + 32DB557922FDADE50016329E /* ServiceTermsModalScreenViewModel.swift in Sources */, 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */, 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, @@ -4264,6 +4327,7 @@ B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */, 3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */, 3232AB4E2256558300AD6A5C /* TemplateScreenViewModelType.swift in Sources */, + 32DB557D22FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift in Sources */, B1B5590520EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, 3232ABA5225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift in Sources */, B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */, @@ -4274,6 +4338,7 @@ B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */, + 32DB557A22FDADE50016329E /* ServiceTermsModalScreenViewController.swift in Sources */, B1B9DEF422EB426D0065E677 /* ReactionHistoryViewCell.swift in Sources */, B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */, B1DCC61B22E5E17100625807 /* EmojiPickerCoordinator.swift in Sources */, @@ -4447,6 +4512,7 @@ B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */, B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */, B1B5575C20EE6C4D00210D55 /* DirectoryViewController.m in Sources */, + 32DB557622FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift in Sources */, B1B558BD20EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */, B1B5577020EE702800210D55 /* WidgetPickerViewController.m in Sources */, B1B558D320EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.m in Sources */, @@ -4462,6 +4528,7 @@ 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */, B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, + 32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */, F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */, 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */, @@ -4470,6 +4537,7 @@ 3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */, 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */, + 32DB557E22FDADE50016329E /* ServiceTermsModalScreenViewAction.swift in Sources */, 32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index cc51f3107..9abf487b1 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -691,6 +691,10 @@ "gdpr_consent_not_given_alert_message" = "To continue using the %@ homeserver you must review and agree to the terms and conditions."; "gdpr_consent_not_given_alert_review_now_action" = "Review now"; +// Service terms +"service_terms_modal_title" = "Terms Of Service"; +"service_terms_modal_accept_button" = "Accept"; + // Deactivate account "deactivate_account_title" = "Deactivate Account"; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 28bbe13d8..07cadf8f5 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -92,6 +92,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: RoomContextualMenuViewController.self) } + internal enum ServiceTermsModalScreenViewController: StoryboardType { + internal static let storyboardName = "ServiceTermsModalScreenViewController" + + internal static let initialScene = InitialSceneType(storyboard: ServiceTermsModalScreenViewController.self) + } internal enum SimpleScreenTemplateViewController: StoryboardType { internal static let storyboardName = "SimpleScreenTemplateViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 27ddc0ec6..4bf3b7bcd 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2342,6 +2342,14 @@ internal enum VectorL10n { internal static var sending: String { return VectorL10n.tr("Vector", "sending") } + /// Accept + internal static var serviceTermsModalAcceptButton: String { + return VectorL10n.tr("Vector", "service_terms_modal_accept_button") + } + /// Terms Of Service + internal static var serviceTermsModalTitle: String { + return VectorL10n.tr("Vector", "service_terms_modal_title") + } /// Add email address internal static var settingsAddEmailAddress: String { return VectorL10n.tr("Vector", "settings_add_email_address") diff --git a/Riot/Modules/Integrations/IntegrationManagerViewController.m b/Riot/Modules/Integrations/IntegrationManagerViewController.m index ff9df08fe..ba8de3ba2 100644 --- a/Riot/Modules/Integrations/IntegrationManagerViewController.m +++ b/Riot/Modules/Integrations/IntegrationManagerViewController.m @@ -26,7 +26,7 @@ NSString *const kIntegrationManagerMainScreen = nil; NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; -@interface IntegrationManagerViewController () +@interface IntegrationManagerViewController () { MXSession *mxSession; NSString *roomId; @@ -37,6 +37,8 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; MXHTTPOperation *operation; } +@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter; + @end @implementation IntegrationManagerViewController @@ -67,9 +69,9 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; operation = nil; } -- (void)viewWillAppear:(BOOL)animated +- (void)viewDidLoad { - [super viewWillAppear:animated]; + [super viewDidLoad]; if (!self.URL && !operation) { @@ -94,9 +96,17 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; self->operation = nil; [self stopActivityIndicator]; - [self withdrawViewControllerAnimated:YES completion:^{ - [[AppDelegate theDelegate] showErrorAsAlert:error]; - }]; + if ([error.domain isEqualToString:WidgetManagerErrorDomain] + && error.code == WidgetManagerErrorCodeTermsNotSigned) + { + [self presentTerms]; + } + else + { + [self withdrawViewControllerAnimated:YES completion:^{ + [[AppDelegate theDelegate] showErrorAsAlert:error]; + }]; + } }]; } } @@ -681,4 +691,59 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; }]; } + +#pragma mark - Service terms +- (void)presentTerms +{ + // Same comment as https://github.com/matrix-org/matrix-react-sdk/blob/1b0d8510a2ee93beddcd34c2d5770aa9fc76b1d9/src/ScalarAuthClient.js#L108 + // The terms endpoints are new and so live on standard _matrix prefixes, + // but IM rest urls are currently configured with paths, so remove the + // path from the base URL before passing it to the js-sdk + + // We continue to use the full URL for the calls done by + // Riot-iOS, but the standard terms API called + // by the matrix-ios-sdk lives on the standard _matrix path. This means we + // don't support running IMs on a non-root path, but it's the only + // realistic way of transitioning to _matrix paths since configs in + // the wild contain bits of the API path. + + // Once we've fully transitioned to _matrix URLs, we can give people + // a grace period to update their configs, then use the rest url as + // a regular base url. + NSURL *imApiUrl = [NSURL URLWithString:[[WidgetManager sharedManager] configForUser:mxSession.myUser.userId].apiUrl]; + NSString *baseUrl = [NSURL URLWithString:@"/" relativeToURL:imApiUrl].absoluteString; + if ([baseUrl hasSuffix:@"/"]) + { + // SDK doest not like trailing / + baseUrl = [baseUrl substringToIndex:baseUrl.length - 1]; + } + + NSLog(@"[IntegraionManagerVC] presentTerms for %@", baseUrl); + + ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter = [[ServiceTermsModalCoordinatorBridgePresenter alloc] initWithSession:mxSession baseUrl:baseUrl + serviceType:MXServiceTypeIntegrationManager + accessToken:scalarToken]; + + serviceTermsModalCoordinatorBridgePresenter.delegate = self; + + [serviceTermsModalCoordinatorBridgePresenter presentFrom:self animated:YES]; + self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter; +} + +- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:^{ + [self withdrawViewControllerAnimated:YES completion:nil]; + }]; + self.serviceTermsModalCoordinatorBridgePresenter = nil; +} + +- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:^{ + [self withdrawViewControllerAnimated:YES completion:nil]; + }]; + self.serviceTermsModalCoordinatorBridgePresenter = nil; +} + @end diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinator.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinator.swift new file mode 100644 index 000000000..3ae1b0ea0 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinator.swift @@ -0,0 +1,69 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 +import UIKit + +final class ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private var serviceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelType + private let serviceTermsModalScreenViewController: ServiceTermsModalScreenViewController + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + // MARK: Public + + weak var delegate: ServiceTermsModalScreenCoordinatorDelegate? + + // MARK: - Setup + + init(serviceTerms: MXServiceTerms) { + + let serviceTermsModalScreenViewModel = ServiceTermsModalScreenViewModel(serviceTerms: serviceTerms) + let serviceTermsModalScreenViewController = ServiceTermsModalScreenViewController.instantiate(with: serviceTermsModalScreenViewModel) + self.serviceTermsModalScreenViewModel = serviceTermsModalScreenViewModel + self.serviceTermsModalScreenViewController = serviceTermsModalScreenViewController + } + + // MARK: - Public methods + + func start() { + self.serviceTermsModalScreenViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.serviceTermsModalScreenViewController + } +} + +// MARK: - ServiceTermsModalScreenViewModelCoordinatorDelegate +extension ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenViewModelCoordinatorDelegate { + + func ServiceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType) { + self.delegate?.ServiceTermsModalScreenCoordinatorDidAccept(self) + } + + func ServiceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType) { + self.delegate?.ServiceTermsModalScreenCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift new file mode 100644 index 000000000..b676963d3 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 + +protocol ServiceTermsModalScreenCoordinatorDelegate: class { + func ServiceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType) + func ServiceTermsModalScreenCoordinatorDidCancel(_ coordinator: ServiceTermsModalScreenCoordinatorType) +} + +/// `ServiceTermsModalScreenCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol ServiceTermsModalScreenCoordinatorType: Coordinator, Presentable { + var delegate: ServiceTermsModalScreenCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift new file mode 100644 index 000000000..163bd73c1 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 + +/// ServiceTermsModalScreenViewController view actions exposed to view model +enum ServiceTermsModalScreenViewAction { + case load + case accept + case cancel +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard new file mode 100644 index 000000000..e1170d964 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.swift new file mode 100644 index 000000000..ab14782d4 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.swift @@ -0,0 +1,175 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 + +final class ServiceTermsModalScreenViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private weak var okButton: UIButton! + + // MARK: Private + + private var viewModel: ServiceTermsModalScreenViewModelType! + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: ServiceTermsModalScreenViewModelType) -> ServiceTermsModalScreenViewController { + let viewController = StoryboardScene.ServiceTermsModalScreenViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = VectorL10n.serviceTermsModalTitle + + self.setupViews() + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .load) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: + self.messageLabel.textColor = theme.textPrimaryColor + + self.okButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.okButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + self.messageLabel.text = "VectorL10n.ServiceTermsModalScreenTitle" + self.messageLabel.isHidden = true + } + + private func render(viewState: ServiceTermsModalScreenViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded(let policies): + self.renderLoaded(policies: policies) + case .accepted: + self.renderAccepted() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded(policies: [MXLoginPolicyData]) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.messageLabel.text = policies.first?.name + self.messageLabel.isHidden = false + } + + private func renderAccepting() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderAccepted() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func acceptButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .accept) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - ServiceTermsModalScreenViewModelViewDelegate +extension ServiceTermsModalScreenViewController: ServiceTermsModalScreenViewModelViewDelegate { + + func ServiceTermsModalScreenViewModel(_ viewModel: ServiceTermsModalScreenViewModelType, didUpdateViewState viewSate: ServiceTermsModalScreenViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModel.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModel.swift new file mode 100644 index 000000000..b683d687e --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModel.swift @@ -0,0 +1,121 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let serviceTerms: MXServiceTerms + + // MARK: Public + + var policies: [MXLoginPolicyData]? + + weak var viewDelegate: ServiceTermsModalScreenViewModelViewDelegate? + weak var coordinatorDelegate: ServiceTermsModalScreenViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(serviceTerms: MXServiceTerms) { + self.serviceTerms = serviceTerms + } + + deinit { + } + + // MARK: - Public + + func process(viewAction: ServiceTermsModalScreenViewAction) { + switch viewAction { + case .load: + self.loadTerms() + case .accept: + self.acceptTerms() + case .cancel: + self.coordinatorDelegate?.ServiceTermsModalScreenViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func loadTerms() { + + self.update(viewState: .loading) + + self.serviceTerms.terms({ [weak self] terms in + guard let self = self else { + return + } + + let policies = self.processTerms(terms: terms) + self.policies = policies + self.update(viewState: .loaded(policies)) + + }, failure: { [weak self] error in + guard let self = self else { + return + } + + self.update(viewState: .error(error)) + }) + } + + private func acceptTerms() { + + self.update(viewState: .loading) + + self.serviceTerms.agree(toTerms: self.termsUrls, success: { [weak self] in + guard let self = self else { + return + } + self.update(viewState: .accepted) + self.coordinatorDelegate?.ServiceTermsModalScreenViewModelDidAccept(self) + + }, failure: { [weak self] (error) in + guard let self = self else { + return + } + + self.update(viewState: .error(error)) + }) + } + + private func processTerms(terms: MXLoginTerms?) -> [MXLoginPolicyData] { + if let policies = terms?.policiesData(forLanguage: Bundle.mxk_language(), defaultLanguage: Bundle.mxk_fallbackLanguage()) { + return policies + } else { + print("[ServiceTermsModalScreenViewModel] processTerms: Error: No terms for \(String(describing: terms))") + return [] + } + } + + private var termsUrls: [String] { + guard let policies = self.policies else { + return [] + } + return policies.map({ return $0.url }) + } + + private func update(viewState: ServiceTermsModalScreenViewState) { + self.viewDelegate?.ServiceTermsModalScreenViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift new file mode 100644 index 000000000..a98c122a7 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift @@ -0,0 +1,39 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 + +protocol ServiceTermsModalScreenViewModelViewDelegate: class { + func ServiceTermsModalScreenViewModel(_ viewModel: ServiceTermsModalScreenViewModelType, didUpdateViewState viewSate: ServiceTermsModalScreenViewState) +} + +protocol ServiceTermsModalScreenViewModelCoordinatorDelegate: class { + func ServiceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType) + func ServiceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType) +} + +/// Protocol describing the view model used by `ServiceTermsModalScreenViewController` +protocol ServiceTermsModalScreenViewModelType { + + var policies: [MXLoginPolicyData]? { get set } + + var viewDelegate: ServiceTermsModalScreenViewModelViewDelegate? { get set } + var coordinatorDelegate: ServiceTermsModalScreenViewModelCoordinatorDelegate? { get set } + + func process(viewAction: ServiceTermsModalScreenViewAction) +} diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewState.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewState.swift new file mode 100644 index 000000000..ef3d7a3f6 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewState.swift @@ -0,0 +1,27 @@ +// File created from ScreenTemplate +// $ createScreen.sh Modal/Show ServiceTermsModalScreen +/* + 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 + +/// ServiceTermsModalScreenViewController view state +enum ServiceTermsModalScreenViewState { + case loading + case loaded([MXLoginPolicyData]) + case accepted + case error(Error) +} diff --git a/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinator.swift b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinator.swift new file mode 100644 index 000000000..46057fc86 --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinator.swift @@ -0,0 +1,80 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalScreen +/* + 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 ServiceTermsModalCoordinator: ServiceTermsModalCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let session: MXSession + private let serviceTerms: MXServiceTerms + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: ServiceTermsModalCoordinatorDelegate? + + // MARK: - Setup + init(session: MXSession, baseUrl: String, serviceType: MXServiceType, accessToken: String) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.session = session + self.serviceTerms = MXServiceTerms(baseUrl: baseUrl, serviceType: serviceType, matrixSession: session, accessToken: accessToken) + } + + // MARK: - Public methods + + func start() { + let rootCoordinator = self.createServiceTermsModalLoadTermsScreenCoordinator() + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + self.navigationRouter.setRootModule(rootCoordinator) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createServiceTermsModalLoadTermsScreenCoordinator() -> ServiceTermsModalScreenCoordinator { + let coordinator = ServiceTermsModalScreenCoordinator(serviceTerms: self.serviceTerms) + coordinator.delegate = self + return coordinator + } +} + +// MARK: - ServiceTermsModalLoadTermsScreenCoordinatorDelegate +extension ServiceTermsModalCoordinator: ServiceTermsModalScreenCoordinatorDelegate { + func ServiceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType) { + self.delegate?.serviceTermsModalCoordinatorDidAccept(self) + } + + func ServiceTermsModalScreenCoordinatorDidCancel(_ coordinator: ServiceTermsModalScreenCoordinatorType) { + self.delegate?.serviceTermsModalCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorBridgePresenter.swift b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..22cda942b --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorBridgePresenter.swift @@ -0,0 +1,95 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalLoadTermsScreen +/* + 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 ServiceTermsModalCoordinatorBridgePresenterDelegate { + func serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter) + func serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter) +} + +/// ServiceTermsModalCoordinatorBridgePresenter enables to start ServiceTermsModalCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class ServiceTermsModalCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let baseUrl: String + private let serviceType: MXServiceType + private let accessToken: String + private var coordinator: ServiceTermsModalCoordinator? + + // MARK: Public + + weak var delegate: ServiceTermsModalCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession, baseUrl: String, serviceType: MXServiceType, accessToken: String) { + self.session = session + self.baseUrl = baseUrl + self.serviceType = serviceType + self.accessToken = accessToken + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let serviceTermsModalCoordinator = ServiceTermsModalCoordinator(session: self.session, baseUrl: self.baseUrl, serviceType: self.serviceType, accessToken: accessToken) + serviceTermsModalCoordinator.delegate = self + viewController.present(serviceTermsModalCoordinator.toPresentable(), animated: animated, completion: nil) + serviceTermsModalCoordinator.start() + + self.coordinator = serviceTermsModalCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + if let completion = completion { + completion() + } + } + } +} + +// MARK: - ServiceTermsModalCoordinatorDelegate +extension ServiceTermsModalCoordinatorBridgePresenter: ServiceTermsModalCoordinatorDelegate { + + func serviceTermsModalCoordinatorDidAccept(_ coordinator: ServiceTermsModalCoordinatorType) { + self.delegate?.serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept(self) + } + + func serviceTermsModalCoordinatorDidCancel(_ coordinator: ServiceTermsModalCoordinatorType) { + self.delegate?.serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel(self) + } +} diff --git a/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift new file mode 100644 index 000000000..27f7b4f8d --- /dev/null +++ b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalLoadTermsScreen +/* + 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 + +protocol ServiceTermsModalCoordinatorDelegate: class { + func serviceTermsModalCoordinatorDidAccept(_ coordinator: ServiceTermsModalCoordinatorType) + func serviceTermsModalCoordinatorDidCancel(_ coordinator: ServiceTermsModalCoordinatorType) +} + +/// `ServiceTermsModalCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol ServiceTermsModalCoordinatorType: Coordinator, Presentable { + var delegate: ServiceTermsModalCoordinatorDelegate? { get } +} From 2c42b055cafff4fffe53c62e15315a64fcce18e2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 9 Aug 2019 17:58:02 +0200 Subject: [PATCH 03/11] IM: Display terms in table view --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 + .../ServiceTermsModalScreenViewAction.swift | 1 + ...eTermsModalScreenViewController.storyboard | 50 ++++++-- ...erviceTermsModalScreenViewController.swift | 109 +++++++++++++++--- .../ServiceTermsModalScreenViewModel.swift | 3 + 6 files changed, 141 insertions(+), 27 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9abf487b1..84c55490c 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -693,6 +693,7 @@ // Service terms "service_terms_modal_title" = "Terms Of Service"; +"service_terms_modal_message" = "To continue you need to accept the Terms of this service."; "service_terms_modal_accept_button" = "Accept"; // Deactivate account diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 4bf3b7bcd..400c25f10 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2346,6 +2346,10 @@ internal enum VectorL10n { internal static var serviceTermsModalAcceptButton: String { return VectorL10n.tr("Vector", "service_terms_modal_accept_button") } + /// To continue you need to accept the Terms of this service. + internal static var serviceTermsModalMessage: String { + return VectorL10n.tr("Vector", "service_terms_modal_message") + } /// Terms Of Service internal static var serviceTermsModalTitle: String { return VectorL10n.tr("Vector", "service_terms_modal_title") diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift index 163bd73c1..5ee84b4df 100644 --- a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewAction.swift @@ -21,6 +21,7 @@ import Foundation /// ServiceTermsModalScreenViewController view actions exposed to view model enum ServiceTermsModalScreenViewAction { case load + case review(String) case accept case cancel } diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard index e1170d964..14f783356 100644 --- a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewController.storyboard @@ -1,6 +1,10 @@ + + + + @@ -11,29 +15,40 @@ - + - + - + - + -