diff --git a/CHANGES.rst b/CHANGES.rst index f67b47833..96d6b7fc7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,44 @@ +Changes in 0.8.0 (2019-02-15) +=============================================== + +Improvements: + * Upgrade MatrixKit version (v0.9.5 - https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.9.5). + * Theming: Create ThemeService to make theming easier. Use it to reskin Riot. + * Use modern literals and array/dictionary syntax where possible (PR #2160). + * Add SwiftGen pod in order to generate Swift constants for assets (#2177). + * RoomVC: Remove the beta warning modal when opening an e2e room (#2239). + * RoomVC: `Redact` has been renamed to `Remove` to match riot/web (#2134). + * Clean up iOS version checking (#2190). + * Key backup: Implement setup screen (#2198). + * Key backup: Implement recover screen (#2196). + * Key backup: Add a dedicated section to settings (#2193). + * Key backup: Implement setup reminder (#2211). + * Key backup: Implement recover reminder (#2206). + * Key backup: Update key backup setup UI and UX (PR #2243). + * Key backup: Logout warning (#2245). + * Key backup: new recover method detected (#2230). + +Bug fix: + * Use white scroll bar on dark themes (#2158). + * Registration: fix tap gesture on checkboxes in the terms screen. + * Registration: improve validation UX on the terms screen (#2164). + * Registration: improve scrolling on the reCaptcha screen (#2165). + * Infinite loading wheel when taping on a fake room alias (#679). + * Ban and kick reasons are silently discarded (#2162). + * Room Version Upgrade: Clicking the link in the room continuation event to go back to the old version of the room doesn't work (#2179). + * Share extension: Fail to send screenshot (#2168). + * Share extension: Handle rich item sharing (image + text + URL) (#2224). + * Share extension: Sharing pages from Firefox only shares their title (#2163). + * Share extension: Fix unloaded theme (PR #2235). + * Reskin: Jump to first unread message doesn't show up in 0.7.12 TF (#2218). + * Reskin: Sometimes the roomVC navigation bar is tranparent (#2252). + Changes in 0.7.11 (2019-01-08) =============================================== Improvements: -* Upgrade MatrixKit version (v0.9.3). -* Fix almost all the warnings caused by -Wstrict-prototypes, thanks to @fridtjof (PR #2155). + * Upgrade MatrixKit version (v0.9.3). + * Fix almost all the warnings caused by -Wstrict-prototypes, thanks to @fridtjof (PR #2155). Changes in 0.7.10 (2019-01-04) =============================================== @@ -15,10 +50,10 @@ Changes in 0.7.9 (2019-01-04) =============================================== Improvements: -* Upgrade MatrixKit version (v0.9.2). + * Upgrade MatrixKit version (v0.9.2). Bug fix: -* Registration: email or phone number is no more skippable (#2140). + * Registration: email or phone number is no more skippable (#2140). Changes in 0.7.8 (2018-12-12) =============================================== diff --git a/Podfile b/Podfile index 4dc3be87d..92626d5a9 100644 --- a/Podfile +++ b/Podfile @@ -9,7 +9,7 @@ source 'https://github.com/CocoaPods/Specs.git' # Different flavours of pods to MatrixKit # The current MatrixKit pod version -$matrixKitVersion = '0.9.3' +$matrixKitVersion = '0.9.5' # The develop branch version #$matrixKitVersion = 'develop' @@ -76,7 +76,10 @@ abstract_target 'RiotPods' do pod 'OLMKit', :inhibit_warnings => true pod 'cmark', :inhibit_warnings => true pod 'DTCoreText', :inhibit_warnings => true - + pod 'zxcvbn-ios' + + # Tools + pod 'SwiftGen', '~> 6.0' target "Riot" do import_MatrixKit diff --git a/Podfile.lock b/Podfile.lock index f885f7c7c..8a56627bb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -44,38 +44,38 @@ PODS: - HPGrowingTextView (1.1) - libbase58 (0.1.4) - libPhoneNumber-iOS (0.9.13) - - MatrixKit (0.9.3): + - MatrixKit (0.9.5): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixKit/Core (= 0.9.3) - - MatrixSDK (= 0.12.1) - - MatrixKit/AppExtension (0.9.3): + - MatrixKit/Core (= 0.9.5) + - MatrixSDK (= 0.12.2) + - MatrixKit/AppExtension (0.9.5): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - DTCoreText/Extension - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.12.1) - - MatrixKit/Core (0.9.3): + - MatrixSDK (= 0.12.2) + - MatrixKit/Core (0.9.5): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.12.1) - - MatrixSDK (0.12.1): - - MatrixSDK/Core (= 0.12.1) - - MatrixSDK/Core (0.12.1): + - MatrixSDK (= 0.12.2) + - MatrixSDK (0.12.2): + - MatrixSDK/Core (= 0.12.2) + - MatrixSDK/Core (0.12.2): - AFNetworking (~> 3.2.0) - GZIP (~> 1.2.2) - libbase58 (~> 0.1.4) - OLMKit (~> 3.0.0) - Realm (~> 3.11.1) - - MatrixSDK/JingleCallStack (0.12.1): + - MatrixSDK/JingleCallStack (0.12.2): - MatrixSDK/Core - WebRTC (= 63.11.20455) - - MatrixSDK/SwiftSupport (0.12.1): + - MatrixSDK/SwiftSupport (0.12.2): - MatrixSDK/Core - OLMKit (3.0.0): - OLMKit/olmc (= 3.0.0) @@ -93,19 +93,23 @@ PODS: - Reusable/View (= 4.0.5) - Reusable/Storyboard (4.0.5) - Reusable/View (4.0.5) + - SwiftGen (6.0.2) - WebRTC (63.11.20455) + - zxcvbn-ios (1.0.4) DEPENDENCIES: - cmark - DTCoreText - GBDeviceInfo (~> 5.2.0) - - MatrixKit (= 0.9.3) - - MatrixKit/AppExtension (= 0.9.3) + - MatrixKit (= 0.9.5) + - MatrixKit/AppExtension (= 0.9.5) - MatrixSDK/JingleCallStack - MatrixSDK/SwiftSupport - OLMKit - PiwikTracker (from `https://github.com/manuroe/matomo-sdk-ios.git`, branch `feature/CustomVariables`) - Reusable (~> 4.0) + - SwiftGen (~> 6.0) + - zxcvbn-ios SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -123,7 +127,9 @@ SPEC REPOS: - OLMKit - Realm - Reusable + - SwiftGen - WebRTC + - zxcvbn-ios EXTERNAL SOURCES: PiwikTracker: @@ -145,14 +151,16 @@ SPEC CHECKSUMS: HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa - MatrixKit: af9f94f36644bc21b63b114413f7bb510506b7d1 - MatrixSDK: f1f510d467d127a5f7afafaafa1554ac5d602132 + MatrixKit: 97bfda595111fe052ea8dbe74f5f65e2eca104a6 + MatrixSDK: 60a9472eacdf51e5110b8bc7beba844debf992ef OLMKit: 88eda69110489f817d59bcb4353b7c247570aa4f PiwikTracker: 42862c7b13028065c3dfd36b4dc38db8a5765acf Realm: 864477d028db77f7c5a0cba64a4892ad53db128a Reusable: 188be1a54ac0691bc66e5bb24ec6eb91971b315b + SwiftGen: e5b047067aa84082826a0c4f1c4c2ddc95bcf74c WebRTC: f2a6203584745fe53532633397557876b5d71640 + zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: fc0574145026734bcc9f3266b7b92f815ebf97e4 +PODFILE CHECKSUM: e4a36bd00d4da77d377d00e5df6044e3ccce3b4c COCOAPODS: 1.6.0.beta.2 diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 16dcad2ef..6ed5783ba 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -10,17 +10,86 @@ 24CBEC591F0EAD310093EABB /* RiotShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BBE81E7009EC00A9B29C /* MXRoom+Riot.m */; }; 24EEE5A31F23A8C300B3C705 /* AvatarGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC111E7009EC00A9B29C /* AvatarGenerator.m */; }; + 3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3209451121F1C1430088CAA2 /* BlackTheme.swift */; }; + 3209451321F1C1D50088CAA2 /* BlackTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3209451121F1C1430088CAA2 /* BlackTheme.swift */; }; + 32242F0921E8B05F00725742 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0821E8B05F00725742 /* UIColor.swift */; }; + 32242F0A21E8B21300725742 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0821E8B05F00725742 /* UIColor.swift */; }; + 32242F1221E8FBA900725742 /* ThemeService.m in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0C21E8FBA900725742 /* ThemeService.m */; }; + 32242F1321E8FBA900725742 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0D21E8FBA900725742 /* Theme.swift */; }; + 32242F1421E8FBA900725742 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0F21E8FBA900725742 /* DefaultTheme.swift */; }; + 32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F1021E8FBA900725742 /* DarkTheme.swift */; }; + 32242F1621E8FBCC00725742 /* ThemeService.m in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0C21E8FBA900725742 /* ThemeService.m */; }; + 32242F1721E8FBE500725742 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0D21E8FBA900725742 /* Theme.swift */; }; + 32242F1821E8FBF800725742 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0F21E8FBA900725742 /* DefaultTheme.swift */; }; + 32242F1921E8FBFB00725742 /* DarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F1021E8FBA900725742 /* DarkTheme.swift */; }; 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; + 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; }; 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; }; 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; }; + 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */; }; + 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */; }; + 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 */; }; 358DB9429359F97520545D35 /* Pods_RiotPods_RiotShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5856BA7A55E53C0AEAFC084 /* Pods_RiotPods_RiotShareExtension.framework */; }; 89C94E649229EA68AE787E9E /* Pods_RiotPods_Riot.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2510A69B4A681C1FEC36E848 /* Pods_RiotPods_Riot.framework */; }; 926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */; }; 92726A471F58737A004AD26F /* IntentHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 92726A461F58737A004AD26F /* IntentHandler.m */; }; 92726A4B1F58737A004AD26F /* SiriIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 92726A431F58737A004AD26F /* SiriIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 92726A511F587410004AD26F /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92726A501F587410004AD26F /* Intents.framework */; }; + B104C2942203773C00D9F496 /* KeyBackupBannerPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = B104C2932203773B00D9F496 /* KeyBackupBannerPreferences.swift */; }; + B1057789221304EC00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1057788221304EB00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift */; }; + B105778B221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */; }; + B105778D2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105778C2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift */; }; + B105778F2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B105778E2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard */; }; + B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDA21ECE09E000DDA48 /* Strings.swift */; }; + B1098BE121ECE09F000DDA48 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDC21ECE09E000DDA48 /* Images.swift */; }; + B1098BE321ECE09F000DDA48 /* RiotDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BDE21ECE09E000DDA48 /* RiotDefaults.swift */; }; + B1098BE521ECE1FC000DDA48 /* Storyboards.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BE421ECE1FC000DDA48 /* Storyboards.swift */; }; + B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BE721ECFE51000DDA48 /* Coordinator.swift */; }; + B1098BF621ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BEA21ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift */; }; + B1098BF721ECFE65000DDA48 /* PasswordStrength.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BEB21ECFE64000DDA48 /* PasswordStrength.swift */; }; + B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1098BEC21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard */; }; + B1098BF921ECFE65000DDA48 /* KeyBackupSetupCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BED21ECFE64000DDA48 /* KeyBackupSetupCoordinator.swift */; }; + B1098BFA21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BEE21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModel.swift */; }; + B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BEF21ECFE64000DDA48 /* KeyBackupSetupCoordinatorType.swift */; }; + B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1098BF021ECFE64000DDA48 /* PasswordStrengthView.xib */; }; + B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BF121ECFE64000DDA48 /* PasswordStrengthManager.swift */; }; + B1098BFE21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BF221ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift */; }; + B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BF321ECFE64000DDA48 /* PasswordStrengthView.swift */; }; + B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BF421ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.swift */; }; + B1098C0121ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098BF521ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinatorType.swift */; }; + B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098C0821ED07E4000DDA48 /* NavigationRouter.swift */; }; + B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098C0B21ED07E4000DDA48 /* Presentable.swift */; }; + B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098C0C21ED07E4000DDA48 /* NavigationRouterType.swift */; }; + B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10B3B592201DD740072C76B /* KeyBackupBannerCell.swift */; }; + B10B3B5C2201DD740072C76B /* KeyBackupBannerCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B10B3B5A2201DD740072C76B /* KeyBackupBannerCell.xib */; }; + B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1107EC72200B0720038014B /* KeyBackupRecoverSuccessViewController.swift */; }; + B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1107EC92200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard */; }; + B110871D21F087F4003554A5 /* KeyBackupSetupPassphraseViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110871C21F087F4003554A5 /* KeyBackupSetupPassphraseViewState.swift */; }; + B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110871F21F098EF003554A5 /* ActivityIndicatorPresenterType.swift */; }; + B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B110872021F098EF003554A5 /* ActivityIndicatorView.xib */; }; + B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */; }; + B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872221F098F0003554A5 /* ActivityIndicatorView.swift */; }; + B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */; }; + B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */; }; + B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */; }; + B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22021FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift */; }; + B139C22321FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22221FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift */; }; + B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C22421FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift */; }; + B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A121F87F7100E3F5FE /* OperationQueue.swift */; }; + B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */; }; + B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */; }; + B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */; }; + B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */; }; + B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */; }; + B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142922144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift */; }; + B14F143222144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142A22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift */; }; + B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142B22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift */; }; + B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142C22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift */; }; + B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */; }; B1664BC520F4E67600808783 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1664BAD20F4E67500808783 /* FallbackViewController.xib */; }; B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BAE20F4E67500808783 /* FallbackViewController.m */; }; B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BB220F4E67500808783 /* SharePresentingViewController.m */; }; @@ -45,8 +114,6 @@ B169329B20F39E6300746532 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B169329520F39E6300746532 /* Main.storyboard */; }; B16932A320F3A21C00746532 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B169329F20F3A21B00746532 /* main.m */; }; B16932A520F3A21C00746532 /* empty.mm in Sources */ = {isa = PBXBuildFile; fileRef = B16932A220F3A21B00746532 /* empty.mm */; }; - B16932AC20F3A7B100746532 /* RiotDesignValues.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932AB20F3A7B000746532 /* RiotDesignValues.m */; }; - B16932AD20F3A7B100746532 /* RiotDesignValues.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932AB20F3A7B000746532 /* RiotDesignValues.m */; }; B16932B120F3AC9200746532 /* RoomSearchDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932AF20F3AC9200746532 /* RoomSearchDataSource.m */; }; B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932E620F3C37100746532 /* HomeMessagesSearchDataSource.m */; }; B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932E920F3C39000746532 /* UnifiedSearchRecentsDataSource.m */; }; @@ -64,6 +131,8 @@ B169331720F3CBE000746532 /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932F920F3C51900746532 /* RecentCellData.m */; }; B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */; }; B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; }; + B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; }; + B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; }; B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; }; B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; }; B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */; }; @@ -301,7 +370,19 @@ B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5598D20EFC5E400210D55 /* DecryptionFailure.m */; }; B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */; }; B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */; }; + B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2621EF6913000D1D89 /* UIViewController.swift */; }; + B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; }; + B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */; }; + B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */; }; B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */; }; + B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752521EE4E620067973F /* KeyboardAvoider.swift */; }; + B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752621EE4E620067973F /* KeyboardNotification.swift */; }; + B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752921EE52B10067973F /* KeyBackupSetupIntroViewController.swift */; }; + B1D4752C21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1D4752B21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard */; }; + B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368821FB1E20001F3AFF /* UIButton.swift */; }; + B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */; }; + B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */; }; + B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1FDF55F21F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift */; }; CD9C4E21170ABE528E087A51 /* Pods_RiotPods_SiriIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC0B9DB497F579AB1084E3DC /* Pods_RiotPods_SiriIntents.framework */; }; F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F05927C71FDED835009F2A68 /* MXGroup+Riot.m */; }; F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BB0D1E7009EC00A9B29C /* AppDelegate.m */; }; @@ -382,12 +463,20 @@ 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RiotShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 2510A69B4A681C1FEC36E848 /* Pods_RiotPods_Riot.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_Riot.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 319CD7C67A47A3D35959E18F /* Pods-RiotPods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-Riot.release.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.release.xcconfig"; sourceTree = ""; }; + 3209451121F1C1430088CAA2 /* BlackTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackTheme.swift; sourceTree = ""; }; + 32242F0821E8B05F00725742 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; + 32242F0C21E8FBA900725742 /* ThemeService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeService.m; sourceTree = ""; }; + 32242F0D21E8FBA900725742 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + 32242F0F21E8FBA900725742 /* DefaultTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = ""; }; + 32242F1021E8FBA900725742 /* DarkTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkTheme.swift; sourceTree = ""; }; + 32242F1121E8FBA900725742 /* ThemeService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThemeService.h; sourceTree = ""; }; 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = ""; }; 3267EFB620E379FD00FF1CAA /* README.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.rst; sourceTree = ""; }; 3275FD8B21A5A2C500B9C13D /* TermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = ""; }; + 3281BCF62201FA4200F4A383 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; 3284A35020A07C210044F922 /* postMessageAPI.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = postMessageAPI.js; sourceTree = ""; }; 32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = ""; }; 32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -396,6 +485,11 @@ 32BDC9A4211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/InfoPlist.strings; sourceTree = ""; }; 32BDC9A5211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = ""; }; 32BDC9A6211C34C90064AF51 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Vector.strings; sourceTree = ""; }; + 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModel.swift; sourceTree = ""; }; + 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewModelType.swift; sourceTree = ""; }; + 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewState.swift; sourceTree = ""; }; + 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupViewAction.swift; sourceTree = ""; }; + 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKeyBackupTableViewSection.swift; sourceTree = ""; }; 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 = ""; }; @@ -415,6 +509,57 @@ 92726A481F58737A004AD26F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 92726A4F1F587393004AD26F /* SiriIntents.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SiriIntents.entitlements; sourceTree = ""; }; 92726A501F587410004AD26F /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; + B104C2932203773B00D9F496 /* KeyBackupBannerPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupBannerPreferences.swift; sourceTree = ""; }; + B1057788221304EB00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupSuccessFromPassphraseViewController.swift; sourceTree = ""; }; + B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupSuccessFromPassphraseViewController.storyboard; sourceTree = ""; }; + B105778C2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupSuccessFromRecoveryKeyViewController.swift; sourceTree = ""; }; + B105778E2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard; sourceTree = ""; }; + B1098BDA21ECE09E000DDA48 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; + B1098BDC21ECE09E000DDA48 /* Images.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = ""; }; + B1098BDE21ECE09E000DDA48 /* RiotDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RiotDefaults.swift; sourceTree = ""; }; + B1098BE421ECE1FC000DDA48 /* Storyboards.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storyboards.swift; sourceTree = ""; }; + B1098BE721ECFE51000DDA48 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; + B1098BEA21ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseCoordinator.swift; sourceTree = ""; }; + B1098BEB21ECFE64000DDA48 /* PasswordStrength.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStrength.swift; sourceTree = ""; }; + B1098BEC21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupSetupPassphraseViewController.storyboard; sourceTree = ""; }; + B1098BED21ECFE64000DDA48 /* KeyBackupSetupCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupCoordinator.swift; sourceTree = ""; }; + B1098BEE21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewModel.swift; sourceTree = ""; }; + B1098BEF21ECFE64000DDA48 /* KeyBackupSetupCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupCoordinatorType.swift; sourceTree = ""; }; + B1098BF021ECFE64000DDA48 /* PasswordStrengthView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PasswordStrengthView.xib; sourceTree = ""; }; + B1098BF121ECFE64000DDA48 /* PasswordStrengthManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStrengthManager.swift; sourceTree = ""; }; + B1098BF221ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewModelType.swift; sourceTree = ""; }; + B1098BF321ECFE64000DDA48 /* PasswordStrengthView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStrengthView.swift; sourceTree = ""; }; + B1098BF421ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewController.swift; sourceTree = ""; }; + B1098BF521ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseCoordinatorType.swift; sourceTree = ""; }; + B1098C0821ED07E4000DDA48 /* NavigationRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = ""; }; + B1098C0B21ED07E4000DDA48 /* Presentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = ""; }; + B1098C0C21ED07E4000DDA48 /* NavigationRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterType.swift; sourceTree = ""; }; + B10B3B592201DD740072C76B /* KeyBackupBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupBannerCell.swift; sourceTree = ""; }; + B10B3B5A2201DD740072C76B /* KeyBackupBannerCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KeyBackupBannerCell.xib; sourceTree = ""; }; + B1107EC72200B0720038014B /* KeyBackupRecoverSuccessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverSuccessViewController.swift; sourceTree = ""; }; + B1107EC92200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverSuccessViewController.storyboard; sourceTree = ""; }; + B110871C21F087F4003554A5 /* KeyBackupSetupPassphraseViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewState.swift; sourceTree = ""; }; + B110871F21F098EF003554A5 /* ActivityIndicatorPresenterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorPresenterType.swift; sourceTree = ""; }; + B110872021F098EF003554A5 /* ActivityIndicatorView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ActivityIndicatorView.xib; sourceTree = ""; }; + B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorPresenter.swift; sourceTree = ""; }; + B110872221F098F0003554A5 /* ActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorView.swift; sourceTree = ""; }; + B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModel.swift; sourceTree = ""; }; + B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModelType.swift; sourceTree = ""; }; + B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewAction.swift; sourceTree = ""; }; + B139C22021FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewState.swift; sourceTree = ""; }; + B139C22221FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseCoordinatorType.swift; sourceTree = ""; }; + B139C22421FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseCoordinator.swift; sourceTree = ""; }; + B140B4A121F87F7100E3F5FE /* OperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; + B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupCoordinatorBridgePresenter.swift; sourceTree = ""; }; + B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = ""; }; + B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromRecoveryKeyViewController.storyboard; sourceTree = ""; }; + B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModelType.swift; sourceTree = ""; }; + B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift; sourceTree = ""; }; + B14F142922144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewState.swift; sourceTree = ""; }; + B14F142A22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinator.swift; sourceTree = ""; }; + B14F142B22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModel.swift; sourceTree = ""; }; + B14F142C22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewAction.swift; sourceTree = ""; }; + B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewController.swift; sourceTree = ""; }; B1664BAD20F4E67500808783 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = ""; }; B1664BAE20F4E67500808783 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = ""; }; B1664BAF20F4E67500808783 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = ""; }; @@ -475,8 +620,6 @@ B16932A020F3A21B00746532 /* Riot.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Riot.entitlements; sourceTree = ""; }; B16932A120F3A21B00746532 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B16932A220F3A21B00746532 /* empty.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = empty.mm; sourceTree = ""; }; - B16932AA20F3A7B000746532 /* RiotDesignValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RiotDesignValues.h; sourceTree = ""; }; - B16932AB20F3A7B000746532 /* RiotDesignValues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RiotDesignValues.m; sourceTree = ""; }; B16932AF20F3AC9200746532 /* RoomSearchDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomSearchDataSource.m; sourceTree = ""; }; B16932B020F3AC9200746532 /* RoomSearchDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomSearchDataSource.h; sourceTree = ""; }; B16932E520F3C37100746532 /* HomeMessagesSearchDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeMessagesSearchDataSource.h; sourceTree = ""; }; @@ -506,6 +649,8 @@ B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = ""; }; B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = ""; }; B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = ""; }; + B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; + B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = ""; }; B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = ""; }; B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LanguagePickerViewController.m; sourceTree = ""; }; @@ -879,8 +1024,20 @@ B1B5599020EFC5E400210D55 /* Analytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = ""; }; B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DecryptionFailureTracker.m; sourceTree = ""; }; B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RoomPredecessorBubbleCell.xib; sourceTree = ""; }; + B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = ""; }; + B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionKeysExportPresenter.swift; sourceTree = ""; }; B1D250D62118AA0A000F4E93 /* RoomPredecessorBubbleCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomPredecessorBubbleCell.h; sourceTree = ""; }; B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomPredecessorBubbleCell.m; sourceTree = ""; }; + B1D4752521EE4E620067973F /* KeyboardAvoider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardAvoider.swift; sourceTree = ""; }; + B1D4752621EE4E620067973F /* KeyboardNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardNotification.swift; sourceTree = ""; }; + B1D4752921EE52B10067973F /* KeyBackupSetupIntroViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupIntroViewController.swift; sourceTree = ""; }; + B1D4752B21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupIntroViewController.storyboard; sourceTree = ""; }; + B1E5368821FB1E20001F3AFF /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewController.swift; sourceTree = ""; }; + B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromPassphraseViewController.storyboard; sourceTree = ""; }; + B1FDF55F21F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewAction.swift; sourceTree = ""; }; B4B35D08E11507BEE733BBC4 /* Pods-RiotPods-SiriIntents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-SiriIntents.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.debug.xcconfig"; sourceTree = ""; }; BC0B9DB497F579AB1084E3DC /* Pods_RiotPods_SiriIntents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_SiriIntents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D523D0ED52562EDD393BE0F0 /* Pods-RiotPods-RiotShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.release.xcconfig"; sourceTree = ""; }; @@ -972,6 +1129,27 @@ path = RiotShareExtension; sourceTree = ""; }; + 32242F0B21E8FBA900725742 /* Theme */ = { + isa = PBXGroup; + children = ( + 32242F0C21E8FBA900725742 /* ThemeService.m */, + 32242F0D21E8FBA900725742 /* Theme.swift */, + 32242F1121E8FBA900725742 /* ThemeService.h */, + 32242F0E21E8FBA900725742 /* Themes */, + ); + path = Theme; + sourceTree = ""; + }; + 32242F0E21E8FBA900725742 /* Themes */ = { + isa = PBXGroup; + children = ( + 32242F0F21E8FBA900725742 /* DefaultTheme.swift */, + 32242F1021E8FBA900725742 /* DarkTheme.swift */, + 3209451121F1C1430088CAA2 /* BlackTheme.swift */, + ); + path = Themes; + sourceTree = ""; + }; 3233F7291F31F3B4006ACA81 /* libs */ = { isa = PBXGroup; children = ( @@ -996,6 +1174,18 @@ path = js; sourceTree = ""; }; + 32BF994D21FA1C6300698084 /* KeyBackup */ = { + isa = PBXGroup; + children = ( + 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */, + 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */, + 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */, + 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */, + 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */, + ); + path = KeyBackup; + sourceTree = ""; + }; 5FC42FA41F5186AFFB6A2404 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1031,6 +1221,141 @@ name = Pods; sourceTree = ""; }; + B1057787221304A200334B1E /* Success */ = { + isa = PBXGroup; + children = ( + B1057788221304EB00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift */, + B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */, + B105778C2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift */, + B105778E2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard */, + ); + path = Success; + sourceTree = ""; + }; + B1098BD921ECE09E000DDA48 /* Generated */ = { + isa = PBXGroup; + children = ( + B1098BDA21ECE09E000DDA48 /* Strings.swift */, + B1098BDC21ECE09E000DDA48 /* Images.swift */, + B1098BDE21ECE09E000DDA48 /* RiotDefaults.swift */, + B1098BE421ECE1FC000DDA48 /* Storyboards.swift */, + ); + path = Generated; + sourceTree = ""; + }; + B1098BE621ECFE51000DDA48 /* Coordinators */ = { + isa = PBXGroup; + children = ( + B1098BE721ECFE51000DDA48 /* Coordinator.swift */, + ); + path = Coordinators; + sourceTree = ""; + }; + B1098BE921ECFE64000DDA48 /* KeyBackup */ = { + isa = PBXGroup; + children = ( + B1098C0221ECFEAF000DDA48 /* Setup */, + B1FDF56421F726AD00BA3834 /* Recover */, + B1107ECB2201BE800038014B /* Banners */, + B1CE9F042216FB09000FAE6A /* ManualExport */, + ); + path = KeyBackup; + sourceTree = ""; + }; + B1098C0221ECFEAF000DDA48 /* Setup */ = { + isa = PBXGroup; + children = ( + B1098BEF21ECFE64000DDA48 /* KeyBackupSetupCoordinatorType.swift */, + B1098BED21ECFE64000DDA48 /* KeyBackupSetupCoordinator.swift */, + B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */, + B1098C0521ECFF5B000DDA48 /* Intro */, + B1098C0321ECFECC000DDA48 /* Passphrase */, + B1057787221304A200334B1E /* Success */, + ); + path = Setup; + sourceTree = ""; + }; + B1098C0321ECFECC000DDA48 /* Passphrase */ = { + isa = PBXGroup; + children = ( + B1098BF521ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinatorType.swift */, + B1098BEA21ECFE64000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift */, + B1098BF221ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift */, + B1098BEE21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewModel.swift */, + B110871C21F087F4003554A5 /* KeyBackupSetupPassphraseViewState.swift */, + B1FDF55F21F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift */, + B1098BF421ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.swift */, + B1098BEC21ECFE64000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard */, + B1098BF321ECFE64000DDA48 /* PasswordStrengthView.swift */, + B1098BF021ECFE64000DDA48 /* PasswordStrengthView.xib */, + ); + path = Passphrase; + sourceTree = ""; + }; + B1098C0521ECFF5B000DDA48 /* Intro */ = { + isa = PBXGroup; + children = ( + B1D4752921EE52B10067973F /* KeyBackupSetupIntroViewController.swift */, + B1D4752B21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard */, + ); + path = Intro; + sourceTree = ""; + }; + B1098C0721ED07E4000DDA48 /* Routers */ = { + isa = PBXGroup; + children = ( + B1098C0B21ED07E4000DDA48 /* Presentable.swift */, + B1098C0C21ED07E4000DDA48 /* NavigationRouterType.swift */, + B1098C0821ED07E4000DDA48 /* NavigationRouter.swift */, + ); + path = Routers; + sourceTree = ""; + }; + B1107EC62200B0190038014B /* Success */ = { + isa = PBXGroup; + children = ( + B1107EC72200B0720038014B /* KeyBackupRecoverSuccessViewController.swift */, + B1107EC92200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard */, + ); + path = Success; + sourceTree = ""; + }; + B1107ECB2201BE800038014B /* Banners */ = { + isa = PBXGroup; + children = ( + B104C2932203773B00D9F496 /* KeyBackupBannerPreferences.swift */, + B10B3B592201DD740072C76B /* KeyBackupBannerCell.swift */, + B10B3B5A2201DD740072C76B /* KeyBackupBannerCell.xib */, + ); + path = Banners; + sourceTree = ""; + }; + B110871E21F098EF003554A5 /* ActivityIndicator */ = { + isa = PBXGroup; + children = ( + B110871F21F098EF003554A5 /* ActivityIndicatorPresenterType.swift */, + B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */, + B110872221F098F0003554A5 /* ActivityIndicatorView.swift */, + B110872021F098EF003554A5 /* ActivityIndicatorView.xib */, + ); + path = ActivityIndicator; + sourceTree = ""; + }; + B14F142522144F6400FA0595 /* RecoveryKey */ = { + isa = PBXGroup; + children = ( + B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */, + B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */, + B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */, + B14F142922144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift */, + B14F142A22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift */, + B14F142B22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift */, + B14F142C22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift */, + B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */, + ); + path = RecoveryKey; + sourceTree = ""; + }; B1664BAB20F4E67500808783 /* Modules */ = { isa = PBXGroup; children = ( @@ -1143,15 +1468,6 @@ path = SupportingFiles; sourceTree = ""; }; - B16932A620F3A6FA00746532 /* Constants */ = { - isa = PBXGroup; - children = ( - B16932AA20F3A7B000746532 /* RiotDesignValues.h */, - B16932AB20F3A7B000746532 /* RiotDesignValues.m */, - ); - path = Constants; - sourceTree = ""; - }; B16932AE20F3AC9200746532 /* DataSources */ = { isa = PBXGroup; children = ( @@ -1320,6 +1636,7 @@ B1B5596B20EFA85C00210D55 /* EncryptionInfo */, B1B556FD20EE6C4C00210D55 /* RoomKeyRequest */, B1B556B020EE6C4C00210D55 /* BugReport */, + B1098BE921ECFE64000DDA48 /* KeyBackup */, B1B556CD20EE6C4C00210D55 /* Common */, ); path = Modules; @@ -1331,9 +1648,11 @@ B1B5567F20EE6C4C00210D55 /* SettingsViewController.h */, B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */, B1B5578120EF564900210D55 /* Views */, + 32BF994D21FA1C6300698084 /* KeyBackup */, B1B5567B20EE6C4C00210D55 /* Language */, B1B5567820EE6C4C00210D55 /* PhoneCountry */, B1B5568020EE6C4C00210D55 /* DeactivateAccount */, + B1CE9EFB22148681000FAE6A /* SignOut */, ); path = Settings; sourceTree = ""; @@ -1590,6 +1909,8 @@ B1B556D420EE6C4C00210D55 /* SegmentedViewController */, B1B556D820EE6C4C00210D55 /* Recents */, B1B5592720EF7A5D00210D55 /* Cells */, + B1D4752421EE4E620067973F /* KeyboardAvoiding */, + B110871E21F098EF003554A5 /* ActivityIndicator */, ); path = Common; sourceTree = ""; @@ -2329,10 +2650,12 @@ B1B5597C20EFC3DF00210D55 /* Managers */ = { isa = PBXGroup; children = ( + B1FDF56321F68C0700BA3834 /* PasswordStrength */, B1798300211B137B001FD722 /* OnBoarding */, B1B5598B20EFC5E400210D55 /* Analytics */, B1B5598920EFC41100210D55 /* BugReport */, B1B5598A20EFC42100210D55 /* Settings */, + 32242F0B21E8FBA900725742 /* Theme */, B1B5598020EFC3DF00210D55 /* Widgets */, ); path = Managers; @@ -2379,6 +2702,68 @@ path = Analytics; sourceTree = ""; }; + B1CE9EFB22148681000FAE6A /* SignOut */ = { + isa = PBXGroup; + children = ( + B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */, + ); + path = SignOut; + sourceTree = ""; + }; + B1CE9F042216FB09000FAE6A /* ManualExport */ = { + isa = PBXGroup; + children = ( + B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */, + ); + path = ManualExport; + sourceTree = ""; + }; + B1D4752421EE4E620067973F /* KeyboardAvoiding */ = { + isa = PBXGroup; + children = ( + B1D4752521EE4E620067973F /* KeyboardAvoider.swift */, + B1D4752621EE4E620067973F /* KeyboardNotification.swift */, + ); + path = KeyboardAvoiding; + sourceTree = ""; + }; + B1E5368A21FB6FC0001F3AFF /* Passphrase */ = { + isa = PBXGroup; + children = ( + B139C22221FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift */, + B139C22421FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift */, + B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */, + B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */, + B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */, + B139C22021FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift */, + B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */, + B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */, + ); + path = Passphrase; + sourceTree = ""; + }; + B1FDF56321F68C0700BA3834 /* PasswordStrength */ = { + isa = PBXGroup; + children = ( + B1098BEB21ECFE64000DDA48 /* PasswordStrength.swift */, + B1098BF121ECFE64000DDA48 /* PasswordStrengthManager.swift */, + ); + path = PasswordStrength; + sourceTree = ""; + }; + B1FDF56421F726AD00BA3834 /* Recover */ = { + isa = PBXGroup; + children = ( + B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */, + B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */, + B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */, + B1E5368A21FB6FC0001F3AFF /* Passphrase */, + B14F142522144F6400FA0595 /* RecoveryKey */, + B1107EC62200B0190038014B /* Success */, + ); + path = Recover; + sourceTree = ""; + }; F083BB021E7005FD00A9B29C /* RiotTests */ = { isa = PBXGroup; children = ( @@ -2401,8 +2786,10 @@ children = ( F083BB0C1E7009EC00A9B29C /* AppDelegate.h */, F083BB0D1E7009EC00A9B29C /* AppDelegate.m */, - B16932A620F3A6FA00746532 /* Constants */, + B1098BD921ECE09E000DDA48 /* Generated */, F083BBE41E7009EC00A9B29C /* Categories */, + B1098BE621ECFE51000DDA48 /* Coordinators */, + B1098C0721ED07E4000DDA48 /* Routers */, F083BBF21E7009EC00A9B29C /* Model */, B1B5597C20EFC3DF00210D55 /* Managers */, B1B5567620EE6C4C00210D55 /* Modules */, @@ -2413,6 +2800,7 @@ ); path = Riot; sourceTree = ""; + usesTabs = 0; }; F083BB0E1E7009EC00A9B29C /* Assets */ = { isa = PBXGroup; @@ -2460,6 +2848,12 @@ F083BBEC1E7009EC00A9B29C /* UIViewController+RiotSearch.m */, 926FA53D1F4C132000F826C2 /* MXSession+Riot.h */, 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */, + 32242F0821E8B05F00725742 /* UIColor.swift */, + B1CA3A2621EF6913000D1D89 /* UIViewController.swift */, + B1CA3A2821EF692B000D1D89 /* UIView.swift */, + B140B4A121F87F7100E3F5FE /* OperationQueue.swift */, + B1E5368821FB1E20001F3AFF /* UIButton.swift */, + 3281BCF62201FA4200F4A383 /* UIControl.swift */, ); path = Categories; sourceTree = ""; @@ -2513,6 +2907,7 @@ A237FB70534FB8ADA0D7CFEE /* Pods */, ); sourceTree = ""; + usesTabs = 0; }; F094A9A31B78D8F000B1FBBF /* Products */ = { isa = PBXGroup; @@ -2569,6 +2964,7 @@ buildConfigurationList = F094A9C81B78D8F000B1FBBF /* Build configuration list for PBXNativeTarget "Riot" */; buildPhases = ( E34E30CD2CEE4A42FF8081D6 /* [CP] Check Pods Manifest.lock */, + B1098BD821ECD3ED000DDA48 /* 🛠 SwiftGen */, F094A99E1B78D8F000B1FBBF /* Sources */, F094A99F1B78D8F000B1FBBF /* Frameworks */, F094A9A01B78D8F000B1FBBF /* Resources */, @@ -2646,6 +3042,9 @@ com.apple.Push = { enabled = 1; }; + com.apple.iCloud = { + enabled = 1; + }; }; }; F094A9BD1B78D8F000B1FBBF = { @@ -2731,6 +3130,7 @@ B1B558BC20EF768F00210D55 /* RoomMembershipBubbleCell.xib in Resources */, B1B5571F20EE6C4D00210D55 /* ContactDetailsViewController.xib in Resources */, B1B558E220EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.xib in Resources */, + B105778B221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard in Resources */, B1664DA220F4F95800808783 /* Localizable.strings in Resources */, B169328720F3954A00746532 /* SharedImages.xcassets in Resources */, B1664DA120F4F94F00808783 /* InfoPlist.strings in Resources */, @@ -2756,10 +3156,12 @@ B1B558F620EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.xib in Resources */, B1B5574D20EE6C4D00210D55 /* MediaPickerViewController.xib in Resources */, B1B5575020EE6C4D00210D55 /* AuthenticationViewController.xib in Resources */, + B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */, B1B5574320EE6C4D00210D55 /* CallViewController.xib in Resources */, F083BDEA1E7009ED00A9B29C /* ringback.mp3 in Resources */, F083BDF21E7009ED00A9B29C /* GoogleService-Info.plist in Resources */, B1B558E320EF768F00210D55 /* RoomEmptyBubbleCell.xib in Resources */, + B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */, B1B5590420EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.xib in Resources */, B1B558F120EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib in Resources */, B1B557CB20EF5D8000210D55 /* DirectoryServerTableViewCell.xib in Resources */, @@ -2767,9 +3169,12 @@ B1B558D720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib in Resources */, B1B5590820EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.xib in Resources */, F083BDE81E7009ED00A9B29C /* message.mp3 in Resources */, + B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */, B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */, F083BE011E7009ED00A9B29C /* third_party_licenses.html in Resources */, + B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */, B1B5573720EE6C4D00210D55 /* GroupParticipantsViewController.xib in Resources */, + B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */, B1B5573320EE6C4D00210D55 /* GroupHomeViewController.xib in Resources */, B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */, B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */, @@ -2801,6 +3206,7 @@ B1B5572D20EE6C4D00210D55 /* RoomParticipantsViewController.xib in Resources */, B1B5577220EE702800210D55 /* JitsiViewController.xib in Resources */, B1B557D720EF5EA900210D55 /* RoomActivitiesView.xib in Resources */, + B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */, F083BDF31E7009ED00A9B29C /* Images.xcassets in Resources */, B1B5590720EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */, B169329920F39E6300746532 /* LaunchScreen.storyboard in Resources */, @@ -2814,6 +3220,7 @@ B1B557D220EF5E3500210D55 /* MediaAlbumTableCell.xib in Resources */, B1B558C520EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.xib in Resources */, B1B5582B20EF666100210D55 /* DirectoryRecentTableViewCell.xib in Resources */, + B105778F2213052A00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard in Resources */, B1B5590F20EF782800210D55 /* TableViewCellWithPhoneNumberTextField.xib in Resources */, B1B5578520EF564900210D55 /* GroupTableViewCellWithSwitch.xib in Resources */, B1B557B320EF5AEF00210D55 /* EventDetailsView.xib in Resources */, @@ -2827,12 +3234,14 @@ 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */, B1B557A220EF58AD00210D55 /* ContactTableViewCell.xib in Resources */, B1B558EB20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib in Resources */, + B10B3B5C2201DD740072C76B /* KeyBackupBannerCell.xib in Resources */, B1B5581820EF625800210D55 /* PreviewRoomTitleView.xib in Resources */, B1B5583020EF66BA00210D55 /* RoomIdOrAliasTableViewCell.xib in Resources */, B1B558BF20EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.xib in Resources */, B1B558FD20EF768F00210D55 /* RoomIncomingAttachmentBubbleCell.xib in Resources */, B1B558DB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib in Resources */, B1B5572B20EE6C4D00210D55 /* RoomMemberDetailsViewController.xib in Resources */, + B1D4752C21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard in Resources */, B1B558C620EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2888,6 +3297,7 @@ "${BUILT_PRODUCTS_DIR}/cmark/cmark.framework", "${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework", "${BUILT_PRODUCTS_DIR}/libbase58/libbase58.framework", + "${BUILT_PRODUCTS_DIR}/zxcvbn-ios/zxcvbn_ios.framework", "${BUILT_PRODUCTS_DIR}/DTCoreText.default-Extension/DTCoreText.framework", "${BUILT_PRODUCTS_DIR}/MatrixKit-AppExtension/MatrixKit.framework", ); @@ -2909,6 +3319,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cmark.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libbase58.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/zxcvbn_ios.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -2933,6 +3344,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + B1098BD821ECD3ED000DDA48 /* 🛠 SwiftGen */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "🛠 SwiftGen"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${PODS_ROOT}/SwiftGen/bin/swiftgen config run --config Tools/SwiftGen/swiftgen-config.yml\n"; + }; E34E30CD2CEE4A42FF8081D6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2959,15 +3388,20 @@ buildActionMask = 2147483647; files = ( B169328420F38BE300746532 /* SegmentedViewController.m in Sources */, - B16932AD20F3A7B100746532 /* RiotDesignValues.m in Sources */, + 32242F1821E8FBF800725742 /* DefaultTheme.swift in Sources */, B1664BCA20F4E67600808783 /* ShareViewController.m in Sources */, B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */, + 32242F1621E8FBCC00725742 /* ThemeService.m in Sources */, 24EEE5A31F23A8C300B3C705 /* AvatarGenerator.m in Sources */, B1664BCF20F4E67600808783 /* ShareExtensionManager.m in Sources */, + 3209451321F1C1D50088CAA2 /* BlackTheme.swift in Sources */, 24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */, B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */, F0A8955F1F7D1FEA00BD6C2A /* MXRoomSummary+Riot.m in Sources */, + 32242F1721E8FBE500725742 /* Theme.swift in Sources */, B169328320F38AE600746532 /* RiotSettings.swift in Sources */, + 32242F0A21E8B21300725742 /* UIColor.swift in Sources */, + 32242F1921E8FBFB00725742 /* DarkTheme.swift in Sources */, B1664BC820F4E67600808783 /* ShareDataSource.m in Sources */, B1664BCD20F4E67600808783 /* RecentRoomTableViewCell.m in Sources */, B169331720F3CBE000746532 /* RecentCellData.m in Sources */, @@ -2988,19 +3422,21 @@ buildActionMask = 2147483647; files = ( B1B557D120EF5E3500210D55 /* MediaAlbumTableCell.m in Sources */, - B16932AC20F3A7B100746532 /* RiotDesignValues.m in Sources */, B1B557A120EF58AD00210D55 /* ContactTableViewCell.m in Sources */, F083BE021E7009ED00A9B29C /* AvatarGenerator.m in Sources */, B1B5573A20EE6C4D00210D55 /* GroupRoomsViewController.m in Sources */, B1B558F920EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m in Sources */, + B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */, B1B5573120EE6C4D00210D55 /* BugReportViewController.m in Sources */, B16932A520F3A21C00746532 /* empty.mm in Sources */, + B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */, B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, B169330820F3CA0E00746532 /* ContactsDataSource.m in Sources */, B1B5574B20EE6C4D00210D55 /* MediaAlbumContentViewController.m in Sources */, B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */, + B1057789221304EC00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift in Sources */, B16932B120F3AC9200746532 /* RoomSearchDataSource.m in Sources */, B16932A320F3A21C00746532 /* main.m in Sources */, B1B5574520EE6C4D00210D55 /* StartChatViewController.m in Sources */, @@ -3009,10 +3445,14 @@ B1B5574820EE6C4D00210D55 /* PeopleViewController.m in Sources */, B1B5598720EFC3E000210D55 /* Widget.m in Sources */, B1B557E320EF60B900210D55 /* MessagesSearchResultAttachmentBubbleCell.m in Sources */, + B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */, B1B5574420EE6C4D00210D55 /* CallViewController.m in Sources */, B1B5572220EE6C4D00210D55 /* RoomSettingsViewController.m in Sources */, B1B5577320EE702800210D55 /* JitsiViewController.m in Sources */, B169331620F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m in Sources */, + B110871D21F087F4003554A5 /* KeyBackupSetupPassphraseViewState.swift in Sources */, + B1098C0121ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinatorType.swift in Sources */, + 32242F1221E8FBA900725742 /* ThemeService.m in Sources */, B1B558E820EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B558F320EF768F00210D55 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B557BD20EF5B4500210D55 /* KeyboardGrowingTextView.m in Sources */, @@ -3021,29 +3461,44 @@ F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */, B1B5598620EFC3E000210D55 /* RiotSettings.swift in Sources */, B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */, + B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */, B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */, B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169330B20F3CA3A00746532 /* Contact.m in Sources */, + B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */, B1B5599220EFC5E400210D55 /* Analytics.m in Sources */, + B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */, + B1098BF621ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift in Sources */, B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */, + B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */, + 32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */, + B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */, B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */, + B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */, B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */, B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */, B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */, + 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */, + B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */, B1B558E120EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.m in Sources */, B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */, + B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */, B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */, B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B5573E20EE6C4D00210D55 /* RiotNavigationController.m in Sources */, B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */, B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */, + B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */, B1B558E920EF768F00210D55 /* RoomSelectedStickerBubbleCell.m in Sources */, B1B558DF20EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, F083BE041E7009ED00A9B29C /* Tools.m in Sources */, 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */, + B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */, + B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */, B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */, + 3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */, B1B5572720EE6C4D00210D55 /* RoomSearchViewController.m in Sources */, F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */, B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */, @@ -3054,8 +3509,13 @@ B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */, B1B5590520EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */, + B1098BE521ECE1FC000DDA48 /* Storyboards.swift in Sources */, + B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, + B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */, B1B5592C20EF7A5D00210D55 /* TableViewCellWithButton.m in Sources */, + 32242F1421E8FBA900725742 /* DefaultTheme.swift in Sources */, + 32242F1321E8FBA900725742 /* Theme.swift in Sources */, B1B5582520EF638A00210D55 /* RoomMemberTitleView.m in Sources */, B1B5582C20EF666100210D55 /* DirectoryRecentTableViewCell.m in Sources */, B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */, @@ -3065,18 +3525,23 @@ B1B558C920EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571B20EE6C4D00210D55 /* DeactivateAccountViewController.m in Sources */, B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */, + B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */, B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */, B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */, + B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */, B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */, B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, + B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */, F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, + B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */, B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */, + B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */, B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */, B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, @@ -3085,11 +3550,15 @@ B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */, B1B5574020EE6C4D00210D55 /* SegmentedViewController.m in Sources */, B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */, + B1098BF921ECFE65000DDA48 /* KeyBackupSetupCoordinator.swift in Sources */, + B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */, + B1098BFE21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift in Sources */, B1B558BE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */, F083BDED1E7009ED00A9B29C /* MXKRoomBubbleTableViewCell+Riot.m in Sources */, B1B557A820EF5A1B00210D55 /* DeviceTableViewCell.m in Sources */, B1B5572620EE6C4D00210D55 /* RoomFilesSearchViewController.m in Sources */, B1B5583120EF66BA00210D55 /* RoomIdOrAliasTableViewCell.m in Sources */, + B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */, F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */, B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */, B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */, @@ -3099,8 +3568,10 @@ B1B5574120EE6C4D00210D55 /* RecentsViewController.m in Sources */, B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */, B1B5577120EE702800210D55 /* StickerPickerViewController.m in Sources */, + B104C2942203773C00D9F496 /* KeyBackupBannerPreferences.swift in Sources */, B1B5572020EE6C4D00210D55 /* ContactsTableViewController.m in Sources */, B1B5581920EF625800210D55 /* RoomTitleView.m in Sources */, + B1098BE321ECE09F000DDA48 /* RiotDefaults.swift in Sources */, B1B557BE20EF5B4500210D55 /* RoomInputToolbarView.m in Sources */, B1B5573B20EE6C4D00210D55 /* FavouritesViewController.m in Sources */, B1B5579920EF575B00210D55 /* AuthInputsView.m in Sources */, @@ -3108,16 +3579,24 @@ B1B5571E20EE6C4D00210D55 /* ContactDetailsViewController.m in Sources */, B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */, B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */, + B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */, + B1098BFA21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModel.swift in Sources */, B1B5575220EE6C4D00210D55 /* RoomKeyRequestViewController.m in Sources */, F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */, B1B558E620EF768F00210D55 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, + B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */, + B1098BF721ECFE65000DDA48 /* PasswordStrength.swift in Sources */, + B105778D2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift in Sources */, B1B557D820EF5EA900210D55 /* RoomActivitiesView.m in Sources */, B1B5596620EF9E9B00210D55 /* RoomTableViewCell.m in Sources */, + B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */, B1B558D020EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B558CF20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, + B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */, B1B5575120EE6C4D00210D55 /* AuthenticationViewController.m in Sources */, B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */, B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */, + B1098BE121ECE09F000DDA48 /* Images.swift in Sources */, B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */, B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */, B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, @@ -3127,12 +3606,22 @@ B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */, B1B557C620EF5CD400210D55 /* DirectoryServerDetailTableViewCell.m in Sources */, B1B5590920EF768F00210D55 /* RoomEmptyBubbleCell.m in Sources */, + B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */, B1B5574720EE6C4D00210D55 /* UsersDevicesViewController.m in Sources */, + B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */, B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */, B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */, B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */, B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */, + B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */, B1B557E920EF60F500210D55 /* MessagesSearchResultTextMsgBubbleCell.m in Sources */, + B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */, + B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */, + B139C22321FF01B200BB68EC /* KeyBackupRecoverFromPassphraseCoordinatorType.swift in Sources */, + B14F143222144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift in Sources */, + B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */, + B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */, + B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */, B169330320F3C98900746532 /* RoomBubbleCellData.m in Sources */, B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */, B1B5575C20EE6C4D00210D55 /* DirectoryViewController.m in Sources */, @@ -3141,23 +3630,34 @@ B1B558D320EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.m in Sources */, B1B5576F20EE702800210D55 /* IntegrationManagerViewController.m in Sources */, B1B557AC20EF5A6D00210D55 /* DeviceView.m in Sources */, + 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */, B16932EE20F3C3C900746532 /* FilesSearchCellData.m in Sources */, B1B558E520EF768F00210D55 /* RoomMembershipExpandedBubbleCell.m in Sources */, + 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */, + 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */, B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, B1B5572C20EE6C4D00210D55 /* RoomParticipantsViewController.m in Sources */, B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, + 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */, + B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */, + B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */, B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */, B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, + 32242F0921E8B05F00725742 /* UIColor.swift in Sources */, B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */, B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */, B1B5577D20EE84BF00210D55 /* CircleButton.m in Sources */, + 32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */, B1B558FF20EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.m in Sources */, + B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */, B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */, + B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */, + B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Riot/AppDelegate.h b/Riot/AppDelegate.h index 8ab291656..811161010 100644 --- a/Riot/AppDelegate.h +++ b/Riot/AppDelegate.h @@ -24,7 +24,7 @@ #import "RageShakeManager.h" #import "Analytics.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #pragma mark - Notifications /** @@ -90,6 +90,7 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification; - (void)restoreEmptyDetailsViewController; - (UIAlertController*)showErrorAsAlert:(NSError*)error; +- (UIAlertController*)showAlertWithTitle:(NSString*)title message:(NSString*)message; #pragma mark - Matrix Sessions handling diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index e49b0ed7b..df6253dc8 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -200,7 +200,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN Prompt to ask the user to log in again. */ UIAlertController *cryptoDataCorruptedAlert; - + + /** + Prompt to warn the user about a new backup on the homeserver. + */ + UIAlertController *wrongBackupVersionAlert; + /** The launch animation container view */ @@ -277,7 +282,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN buildBranch = MAKE_NS_STRING(GIT_BRANCH); #endif #ifdef BUILD_NUMBER - buildNumber = [NSString stringWithFormat:@"#%d", BUILD_NUMBER]; + buildNumber = [NSString stringWithFormat:@"#%@", @(BUILD_NUMBER)]; #endif if (buildBranch && buildNumber) { @@ -389,7 +394,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSLog(@"[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@", @([application isProtectedDataAvailable])); // Log app information - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; NSString* appVersion = [AppDelegate theDelegate].appVersion; NSString* build = [AppDelegate theDelegate].build; @@ -403,6 +408,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [self setupUserDefaults]; + // Set up theme + ThemeService.shared.themeId = RiotSettings.shared.userInterfaceTheme; + // Set up runtime language and fallback by considering the userDefaults object shared within the application group. NSUserDefaults *sharedUserDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; NSString *language = [sharedUserDefaults objectForKey:@"appLanguage"]; @@ -422,7 +430,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [NSBundle mxk_setFallbackLanguage:@"en"]; // Define the navigation bar text color - [[UINavigationBar appearance] setTintColor:kRiotColorGreen]; + [[UINavigationBar appearance] setTintColor:ThemeService.shared.theme.tintColor]; // Customize the localized string table [NSBundle mxk_customizeLocalizedStringTableName:@"Vector"]; @@ -437,15 +445,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController; splitViewController.delegate = self; - _masterNavigationController = [splitViewController.viewControllers objectAtIndex:0]; + _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) { - secondNavController.navigationBar.barTintColor = kRiotPrimaryBgColor; - secondNavController.topViewController.view.backgroundColor = kRiotPrimaryBgColor; + [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 @@ -515,6 +523,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [cryptoDataCorruptedAlert dismissViewControllerAnimated:NO completion:nil]; cryptoDataCorruptedAlert = nil; } + + if (wrongBackupVersionAlert) + { + [wrongBackupVersionAlert dismissViewControllerAnimated:NO completion:nil]; + wrongBackupVersionAlert = nil; + } } - (void)applicationDidEnterBackground:(UIApplication *)application @@ -647,7 +661,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Observe crypto data storage corruption [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onSessionCryptoDidCorruptData:) name:kMXSessionCryptoDidCorruptDataNotification object:nil]; - + + // Observe wrong backup version + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBackupStateDidChangeNotification:) name:kMXKeyBackupDidStateChangeNotification object:nil]; + // Resume all existing matrix sessions NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; for (MXKAccount *account in mxAccounts) @@ -786,6 +803,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSLog(@"[AppDelegate] restoreInitialDisplay: keep visible log in again"); [self showNotificationAlert:cryptoDataCorruptedAlert]; } + else if (wrongBackupVersionAlert) + { + NSLog(@"[AppDelegate] restoreInitialDisplay: keep visible wrongBackupVersionAlert"); + [self showNotificationAlert:wrongBackupVersionAlert]; + + } // Check whether an error notification is pending else if (_errorNotification) { @@ -835,7 +858,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; UIViewController *emptyDetailsViewController = [storyboard instantiateViewControllerWithIdentifier:@"EmptyDetailsViewControllerStoryboardId"]; - emptyDetailsViewController.view.backgroundColor = kRiotPrimaryBgColor; + emptyDetailsViewController.view.backgroundColor = ThemeService.shared.theme.backgroundColor; splitViewController.viewControllers = @[mainViewController, emptyDetailsViewController]; } @@ -868,9 +891,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN return nil; } } - - [_errorNotification dismissViewControllerAnimated:NO completion:nil]; - + NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; if (!title) @@ -886,13 +907,26 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } - _errorNotification = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert]; + // Switch in offline mode in case of network reachability error + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet) + { + self.isOffline = YES; + } + + return [self showAlertWithTitle:title message:msg]; +} + +- (UIAlertController*)showAlertWithTitle:(NSString*)title message:(NSString*)message +{ + [_errorNotification dismissViewControllerAnimated:NO completion:nil]; + + _errorNotification = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; [_errorNotification addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - + [AppDelegate theDelegate].errorNotification = nil; - + }]]; // Display the error notification if (!isErrorNotificationSuspended) @@ -900,13 +934,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [_errorNotification mxk_setAccessibilityIdentifier:@"AppDelegateErrorAlert"]; [self showNotificationAlert:_errorNotification]; } - - // Switch in offline mode in case of network reachability error - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet) - { - self.isOffline = YES; - } - + return self.errorNotification; } @@ -974,6 +1002,47 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } +- (void)keyBackupStateDidChangeNotification:(NSNotification *)notification +{ + MXKeyBackup *keyBackup = notification.object; + + if (keyBackup.state == MXKeyBackupStateWrongBackUpVersion) + { + if (wrongBackupVersionAlert) + { + [wrongBackupVersionAlert dismissViewControllerAnimated:NO completion:nil]; + } + + wrongBackupVersionAlert = [UIAlertController + alertControllerWithTitle:NSLocalizedStringFromTable(@"e2e_key_backup_wrong_version_title", @"Vector", nil) + + message:NSLocalizedStringFromTable(@"e2e_key_backup_wrong_version", @"Vector", nil) + + preferredStyle:UIAlertControllerStyleAlert]; + + MXWeakify(self); + [wrongBackupVersionAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"e2e_key_backup_wrong_version_button_settings"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + MXStrongifyAndReturnIfNil(self); + self->wrongBackupVersionAlert = nil; + + // TODO: Open settings + }]]; + + [wrongBackupVersionAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"e2e_key_backup_wrong_version_button_wasme"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + MXStrongifyAndReturnIfNil(self); + self->wrongBackupVersionAlert = nil; + }]]; + + [self showNotificationAlert:wrongBackupVersionAlert]; + } +} + #pragma mark - (void)popToHomeViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion @@ -1069,20 +1138,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN if (!isPushRegistered) { NSMutableSet* notificationCategories = [NSMutableSet set]; - if ([[UIMutableUserNotificationAction class] instancesRespondToSelector:@selector(behavior)]) - { - UIMutableUserNotificationAction* quickReply = [[UIMutableUserNotificationAction alloc] init]; - quickReply.title = NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); - quickReply.identifier = @"inline-reply"; - quickReply.activationMode = UIUserNotificationActivationModeBackground; - quickReply.authenticationRequired = true; - quickReply.behavior = UIUserNotificationActionBehaviorTextInput; - UIMutableUserNotificationCategory* quickReplyCategory = [[UIMutableUserNotificationCategory alloc] init]; - quickReplyCategory.identifier = @"QUICK_REPLY"; - [quickReplyCategory setActions:[NSArray arrayWithObjects:quickReply, nil] forContext:UIUserNotificationActionContextDefault]; - [notificationCategories addObject:quickReplyCategory]; - } + UIMutableUserNotificationAction* quickReply = [[UIMutableUserNotificationAction alloc] init]; + quickReply.title = NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); + quickReply.identifier = @"inline-reply"; + quickReply.activationMode = UIUserNotificationActivationModeBackground; + quickReply.authenticationRequired = true; + quickReply.behavior = UIUserNotificationActionBehaviorTextInput; + + UIMutableUserNotificationCategory* quickReplyCategory = [[UIMutableUserNotificationCategory alloc] init]; + quickReplyCategory.identifier = @"QUICK_REPLY"; + [quickReplyCategory setActions:@[quickReply] forContext:UIUserNotificationActionContextDefault]; + [notificationCategories addObject:quickReplyCategory]; // Registration on iOS 8 and later UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:notificationCategories]; @@ -1144,7 +1211,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else { [manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { - NSString* responseText = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey]; + NSString* responseText = responseInfo[UIUserNotificationActionResponseTypedTextKey]; if (responseText != nil && responseText.length != 0) { NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: sending message to room: %@", roomId); @@ -1914,6 +1981,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } failure:^(NSError *error) { NSLog(@"[AppDelegate] Universal link: Error: The home server failed to resolve the room alias (%@)", roomIdOrAlias); + + [homeViewController stopActivityIndicator]; + + NSString *errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_does_not_exist", @"Vector", nil), roomIdOrAlias]; + + [self showAlertWithTitle:nil message:errorMessage]; }]; } else if ([roomIdOrAlias hasPrefix:@"!"] && ((MXKAccount*)accountManager.activeAccounts.firstObject).mxSession.state != MXSessionStateRunning) @@ -2144,10 +2217,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN for (NSString *keyValue in [fragments[1] componentsSeparatedByString:@"&"]) { // Get the parameter name - NSString *key = [[keyValue componentsSeparatedByString:@"="] objectAtIndex:0]; + NSString *key = [keyValue componentsSeparatedByString:@"="][0]; // Get the parameter value - NSString *value = [[keyValue componentsSeparatedByString:@"="] objectAtIndex:1]; + NSString *value = [keyValue componentsSeparatedByString:@"="][1]; if (value.length) { value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; @@ -2659,6 +2732,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Clear cache [MXMediaManager clearCache]; + // Reset key backup banner preferences + [KeyBackupBannerPreferences.shared reset]; + #ifdef MX_CALL_STACK_ENDPOINT // Erase all created certificates and private keys by MXEndpointCallStack for (MXKAccount *account in MXKAccountManager.sharedManager.accounts) @@ -2825,7 +2901,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN if (!launchAnimationContainerView && window) { launchAnimationContainerView = [[UIView alloc] initWithFrame:window.bounds]; - launchAnimationContainerView.backgroundColor = kRiotPrimaryBgColor; + launchAnimationContainerView.backgroundColor = ThemeService.shared.theme.backgroundColor; launchAnimationContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [window addSubview:launchAnimationContainerView]; @@ -3449,22 +3525,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN _jitsiViewController = nil; - NSError *theError = [NSError errorWithDomain:@"" - code:0 - userInfo:@{ - NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"call_jitsi_error", @"Vector", nil) - }]; - [self showErrorAsAlert:theError]; + [self showAlertWithTitle:nil message:NSLocalizedStringFromTable(@"call_jitsi_error", @"Vector", nil)]; }]; } else { - NSError *error = [NSError errorWithDomain:@"" - code:0 - userInfo:@{ - NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"call_already_displayed", @"Vector", nil) - }]; - [self showErrorAsAlert:error]; + [self showAlertWithTitle:nil message:NSLocalizedStringFromTable(@"call_already_displayed", @"Vector", nil)]; } } @@ -3531,7 +3597,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [_callStatusBarButton setTitle:buttonTitle forState:UIControlStateNormal]; [_callStatusBarButton setTitle:buttonTitle forState:UIControlStateHighlighted]; - _callStatusBarButton.titleLabel.textColor = kRiotPrimaryBgColor; + _callStatusBarButton.titleLabel.textColor = ThemeService.shared.theme.backgroundColor; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { @@ -3542,7 +3608,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN _callStatusBarButton.titleLabel.font = [UIFont boldSystemFontOfSize:17]; } - [_callStatusBarButton setBackgroundColor:kRiotColorGreen]; + [_callStatusBarButton setBackgroundColor:ThemeService.shared.theme.tintColor]; [_callStatusBarButton addTarget:self action:@selector(onCallStatusBarButtonPressed) forControlEvents:UIControlEventTouchUpInside]; // Place button into the new window @@ -3691,7 +3757,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; UIViewController *emptyDetailsViewController = [storyboard instantiateViewControllerWithIdentifier:@"EmptyDetailsViewControllerStoryboardId"]; - emptyDetailsViewController.view.backgroundColor = kRiotPrimaryBgColor; + emptyDetailsViewController.view.backgroundColor = ThemeService.shared.theme.backgroundColor; return emptyDetailsViewController; } @@ -3777,7 +3843,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN callerDisplayname = event.sender; } - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; NSString *message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"no_voip", @"Vector", nil), callerDisplayname, appDisplayName]; diff --git a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon.png b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon.png index 6b01ab285..013c41d5c 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon.png and b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@2x.png b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@2x.png index 82744e7b9..215a080fc 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@2x.png and b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@3x.png b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@3x.png index 141fe8dd1..95624f6a6 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@3x.png and b/Riot/Assets/Images.xcassets/Call/call_audio_mute_on_icon.imageset/call_audio_mute_on_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon.png b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon.png index eaa05dbfa..44864f900 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon.png and b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@2x.png b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@2x.png index dbb70a713..13fefb36c 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@2x.png and b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@3x.png b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@3x.png index 1ec1b990f..39e3c5052 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@3x.png and b/Riot/Assets/Images.xcassets/Call/call_chat_icon.imageset/call_chat_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon.png b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon.png index 65359e9d8..159cf2ea3 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon.png and b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@2x.png b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@2x.png index f9d21fb6f..ca996ebbf 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@2x.png and b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@3x.png b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@3x.png index a7e388f45..383d5dbcd 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@3x.png and b/Riot/Assets/Images.xcassets/Call/call_hangup_icon.imageset/call_hangup_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon.png b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon.png index 95567aa15..db46e4226 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon.png and b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@2x.png b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@2x.png index 0dec0daae..e77a0fe96 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@2x.png and b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@3x.png b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@3x.png index f65eebb86..17aa6b00c 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@3x.png and b/Riot/Assets/Images.xcassets/Call/call_speaker_on_icon.imageset/call_speaker_on_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon.png b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon.png index 50a6c0a74..e4027cf3f 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon.png and b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@2x.png b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@2x.png index 69acdc48f..f52ebfd34 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@2x.png and b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@3x.png b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@3x.png index 1f721ca0f..ccdebb760 100644 Binary files a/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@3x.png and b/Riot/Assets/Images.xcassets/Call/call_video_mute_on_icon.imageset/call_video_mute_on_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch.png b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch.png index 61381bd7b..c5edbd95c 100644 Binary files a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch.png and b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch.png differ diff --git a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@2x.png b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@2x.png index 9b21264dd..b09dc61f8 100644 Binary files a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@2x.png and b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@3x.png b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@3x.png index 10eba70c6..445b5c633 100644 Binary files a/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@3x.png and b/Riot/Assets/Images.xcassets/Call/camera_switch.imageset/camera_switch@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit.png b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit.png index 250f0bc64..3698dba7e 100644 Binary files a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit.png and b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit.png differ diff --git a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@2x.png b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@2x.png index 33ba6e019..4db45b362 100644 Binary files a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@2x.png and b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@3x.png b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@3x.png index 31e4f74fd..d8bfb0eb1 100644 Binary files a/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@3x.png and b/Riot/Assets/Images.xcassets/Call/riot_icon_callkit.imageset/riot_icon_callkit@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon.png b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon.png index e922a69d1..789f53946 100644 Binary files a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon.png and b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@2x.png b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@2x.png index e996da664..beab3807b 100644 Binary files a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@2x.png and b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@3x.png b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@3x.png index 32cdf4b60..ef8620069 100644 Binary files a/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@3x.png and b/Riot/Assets/Images.xcassets/Common/back_icon.imageset/back_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo.png b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo.png index 93f2f59d6..eea53f0e7 100644 Binary files a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo.png and b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo.png differ diff --git a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@2x.png b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@2x.png index 06ebace62..64378a435 100644 Binary files a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@2x.png and b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@3x.png b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@3x.png index ed1628eed..2907add84 100644 Binary files a/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@3x.png and b/Riot/Assets/Images.xcassets/Common/logo.imageset/logo@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder.png b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder.png index 9630bb3c8..72db98d7e 100644 Binary files a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder.png and b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder.png differ diff --git a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@2x.png b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@2x.png index 8e5ef48ae..8429357f7 100644 Binary files a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@2x.png and b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@3x.png b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@3x.png index fba0bea39..856f6268c 100644 Binary files a/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@3x.png and b/Riot/Assets/Images.xcassets/Common/placeholder.imageset/placeholder@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon.png b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon.png index 370c82e62..1f45ad1cc 100644 Binary files a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon.png and b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@2x.png b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@2x.png index 5523ca2c4..fb547057b 100644 Binary files a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@2x.png and b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@3x.png b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@3x.png index ba7980920..d465ca1f4 100644 Binary files a/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@3x.png and b/Riot/Assets/Images.xcassets/Common/remove_icon.imageset/remove_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick.png b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick.png index 634a459bc..f694efee6 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick.png and b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@2x.png b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@2x.png index 8855aa986..8c1e20566 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@2x.png and b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@3x.png b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@3x.png index 540ad7380..643cfeafb 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@3x.png and b/Riot/Assets/Images.xcassets/Common/selection_tick.imageset/selection_tick@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick.png b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick.png index 48317db9f..62ba9621a 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick.png and b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@2x.png b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@2x.png index 168f47a78..f8aa7c49e 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@2x.png and b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@3x.png b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@3x.png index 5b8281e39..4f320f7d8 100644 Binary files a/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@3x.png and b/Riot/Assets/Images.xcassets/Common/selection_untick.imageset/selection_untick@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant.png b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant.png index 6b6424eb9..d98356fe5 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant.png and b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@2x.png b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@2x.png index ec358c389..492f89d34 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@2x.png and b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@3x.png b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@3x.png index 3bd549600..d6813615c 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@3x.png and b/Riot/Assets/Images.xcassets/Communities/add_group_participant.imageset/add_group_participant@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group.png b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group.png index f0faaf5f9..b5c41a287 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group.png and b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@2x.png b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@2x.png index 558e90f24..dd155f0e3 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@2x.png and b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@3x.png b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@3x.png index bd54711b5..5d3297978 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@3x.png and b/Riot/Assets/Images.xcassets/Communities/create_group.imageset/create_group@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue.png b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue.png index 8d8376b96..020dbaa1c 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue.png and b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@2x.png b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@2x.png index 385e51416..42335606c 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@2x.png and b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@3x.png b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@3x.png index f20566ed6..b49790039 100644 Binary files a/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@3x.png and b/Riot/Assets/Images.xcassets/Communities/remove_icon_blue.imageset/remove_icon_blue@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon.png b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon.png index 373a12638..8879f851f 100644 Binary files a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon.png and b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@2x.png b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@2x.png index 84691d7b4..b333a6523 100644 Binary files a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@2x.png and b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@3x.png b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@3x.png index d84b0b5f2..c6ed87c18 100644 Binary files a/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@3x.png and b/Riot/Assets/Images.xcassets/Contacts/riot_icon.imageset/riot_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked.png b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked.png index cc8db8435..afa74eea1 100644 Binary files a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked.png and b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked.png differ diff --git a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@2x.png b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@2x.png index 58eb441b8..6c99a8770 100644 Binary files a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@2x.png and b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@2x.png differ diff --git a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@3x.png b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@3x.png index 93dc7c057..781129944 100644 Binary files a/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@3x.png and b/Riot/Assets/Images.xcassets/E2E/e2e_blocked.imageset/e2e_blocked@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff.png index f1fc0f649..04b10d525 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@2x.png index a3eac676c..31db105b7 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@3x.png index 176f89edc..8759d79fc 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOff.imageset/directChatOff@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn.png index f751c6c06..1d093e3e9 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@2x.png index aa575b59a..0a60d421b 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@3x.png index 4a33d1ea2..d5cfc0049 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/directChatOn.imageset/directChatOn@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite.png index 2eaa7af25..a073830ac 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@2x.png index 3ef541b46..9fb29fbe1 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@3x.png index 6a957e632..032c4c6cf 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favourite.imageset/favourite@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff.png index fd86162d3..fd5c8bd53 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@2x.png index f1b35c28e..13d8b05f1 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@3x.png index f2113cef0..0785e80fb 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/favouriteOff.imageset/favouriteOff@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave.png index 66e45060b..6484d7363 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@2x.png index 9461aaebf..ff74e39f8 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@3x.png index 90d97382f..d0b772d24 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/leave.imageset/leave@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications.png index 4026ffb9f..c349e8be1 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@2x.png index 2903605f3..f8384254f 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@3x.png index eed936962..f9e6ce4e7 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notifications.imageset/notifications@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff.png index daf218e4e..251ee225a 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@2x.png index 9be03b4ba..2ef0716aa 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@3x.png index 0591b69fb..da6a19b1e 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/notificationsOff.imageset/notificationsOff@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh.png index f69c85c91..c84af379d 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@2x.png index b86c2445a..b9121080d 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@3x.png index a9e126e2d..0007d53ca 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityHigh.imageset/priorityHigh@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow.png index bb9d5068b..f49a688ee 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@2x.png index 35472e69f..e67d0beee 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@2x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@3x.png index 11b8e8cfc..17a6cca8c 100644 Binary files a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@3x.png and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/priorityLow.imageset/priorityLow@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room.png b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room.png index 54f1920c8..e8ee4dd19 100644 Binary files a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room.png and b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room.png differ diff --git a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@2x.png b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@2x.png index 6ec374de1..5344e7b50 100644 Binary files a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@2x.png and b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@3x.png b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@3x.png index b085c20e5..23094a5d3 100644 Binary files a/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@3x.png and b/Riot/Assets/Images.xcassets/Home/create_room.imageset/create_room@3x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/Contents.json b/Riot/Assets/Images.xcassets/KeyBackup/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/Riot/Assets/Images.xcassets/KeyBackup/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/Contents.json b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/Contents.json new file mode 100644 index 000000000..ad1c76684 --- /dev/null +++ b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "close_banner.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "close_banner@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "close_banner@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner.png b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner.png new file mode 100644 index 000000000..45b45ddde Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@2x.png b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@2x.png new file mode 100644 index 000000000..d7e5f1cfb Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@2x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@3x.png b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@3x.png new file mode 100644 index 000000000..e0303e17a Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/close_banner.imageset/close_banner@3x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/Contents.json b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/Contents.json new file mode 100644 index 000000000..bab1934ca --- /dev/null +++ b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "import_files_button.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "import_files_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "import_files_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button.png b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button.png new file mode 100644 index 000000000..63b732d56 Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@2x.png b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@2x.png new file mode 100644 index 000000000..d47fe066d Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@2x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@3x.png b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@3x.png new file mode 100644 index 000000000..d69c0126b Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/import_files_button.imageset/import_files_button@3x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/Contents.json b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/Contents.json new file mode 100644 index 000000000..f96088786 --- /dev/null +++ b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "key_backup_logo.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "key_backup_logo@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "key_backup_logo@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo.png b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo.png new file mode 100644 index 000000000..ebdda3c65 Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@2x.png b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@2x.png new file mode 100644 index 000000000..f80879b93 Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@2x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@3x.png b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@3x.png new file mode 100644 index 000000000..ddcc43fe0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/key_backup_logo.imageset/key_backup_logo@3x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/Contents.json b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/Contents.json new file mode 100644 index 000000000..2762fc2b1 --- /dev/null +++ b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "reveal_password_button.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "reveal_password_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "reveal_password_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button.png b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button.png new file mode 100644 index 000000000..eafa300c8 Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@2x.png b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@2x.png new file mode 100644 index 000000000..3e9307b0f Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@2x.png differ diff --git a/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@3x.png b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@3x.png new file mode 100644 index 000000000..31f4c088d Binary files /dev/null and b/Riot/Assets/Images.xcassets/KeyBackup/reveal_password_button.imageset/reveal_password_button@3x.png differ diff --git a/Riot/Assets/Images.xcassets/LaunchScreenRiot.imageset/LaunchScreenRiot.png b/Riot/Assets/Images.xcassets/LaunchScreenRiot.imageset/LaunchScreenRiot.png index ee42954c7..e8d821507 100644 Binary files a/Riot/Assets/Images.xcassets/LaunchScreenRiot.imageset/LaunchScreenRiot.png and b/Riot/Assets/Images.xcassets/LaunchScreenRiot.imageset/LaunchScreenRiot.png differ diff --git a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png index 18fd0da50..388b3a1e8 100644 Binary files a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png and b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png differ diff --git a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@2x.png b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@2x.png index 51eb61360..7bade5e8e 100644 Binary files a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@2x.png and b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@2x.png differ diff --git a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@3x.png b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@3x.png index 2d29d4984..2a466e5d0 100644 Binary files a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@3x.png and b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error.png b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error.png index 949063aa5..6c22b91f7 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error.png and b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@2x.png index 7e0484dbb..9b0a524ef 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@3x.png index e030aa759..ac7e4eca0 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/error.imageset/error@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages.png b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages.png index 334cbf1cd..9695c3547 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages.png and b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@2x.png index 8ada7ef00..ca6cc104f 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@3x.png index 38d861af0..2b9212985 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/newmessages.imageset/newmessages@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png index 37fdca055..d609d1458 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png index d7b64dd32..0c045230d 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png index abeea3f07..be11b41d7 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup.png b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup.png index 51a5cc889..7c105000b 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@2x.png index 7b5351cae..d52039cfa 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@3x.png index f7f4b5814..0237eb3ec 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrollup.imageset/scrollup@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing.png b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing.png index ead80a106..8b57471d5 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing.png and b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@2x.png index 8ba44e832..5766b6eba 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@3x.png index e4aa3ad98..0e75d5408 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/typing.imageset/typing@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png index db35287b8..1622e3b9e 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@2x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@2x.png index 4504684ba..cefdc5133 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@2x.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@3x.png index c4af7fbdf..5ec25ef86 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@3x.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon.imageset/upload_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon.png b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon.png index 382d09080..1b13559b7 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon.png and b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@2x.png b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@2x.png index 324c45e3e..b5a2c6986 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@2x.png and b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@3x.png index 01fb0cdfc..d5274f519 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@3x.png and b/Riot/Assets/Images.xcassets/Room/Input/voice_call_icon.imageset/voice_call_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant.png b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant.png index 1ec5cdeb1..21a651840 100644 Binary files a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant.png and b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant.png differ diff --git a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@2x.png b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@2x.png index f9c99cef9..9383bae0c 100644 Binary files a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@2x.png and b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@3x.png b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@3x.png index 3e5df6479..4f66714cb 100644 Binary files a/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@3x.png and b/Riot/Assets/Images.xcassets/Room/add_participant.imageset/add_participant@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon.png b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon.png index a2751b1eb..4e1ba1b0c 100644 Binary files a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon.png and b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@2x.png b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@2x.png index 6061fa965..f2e7d0d6b 100644 Binary files a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@2x.png and b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@3x.png b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@3x.png index cbad4c960..d30df1600 100644 Binary files a/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@3x.png and b/Riot/Assets/Images.xcassets/Room/apps-icon.imageset/apps-icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon.png b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon.png index 2ac0b1420..c76ad4a9b 100644 Binary files a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon.png and b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@2x.png b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@2x.png index ef21fce9c..151898052 100644 Binary files a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@2x.png and b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@3x.png b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@3x.png index 45155d3ff..eceb5c1e7 100644 Binary files a/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@3x.png and b/Riot/Assets/Images.xcassets/Room/details_icon.imageset/details_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon.png b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon.png index eb23ecd65..8522a9c34 100644 Binary files a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon.png and b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@2x.png b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@2x.png index 8eb7a46ca..c697bde87 100644 Binary files a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@2x.png and b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@3x.png b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@3x.png index ec24ed814..b5af8cb0a 100644 Binary files a/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@3x.png and b/Riot/Assets/Images.xcassets/Room/members_list_icon.imageset/members_list_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink.png b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink.png index 68059933f..186384cea 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink.png and b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@2x.png b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@2x.png index 4824feb84..0735dff9d 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@2x.png and b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@3x.png b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@3x.png index 71cd00734..bf2ba6f6c 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@3x.png and b/Riot/Assets/Images.xcassets/Settings/remove_icon_pink.imageset/remove_icon_pink@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon.png b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon.png index 90274882d..33b57afbb 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon.png and b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@2x.png b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@2x.png index 3d06a4a7d..b3c8d9df3 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@2x.png and b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@3x.png b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@3x.png index 8178e63c7..456c1c67f 100644 Binary files a/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@3x.png and b/Riot/Assets/Images.xcassets/Settings/settings_icon.imageset/settings_icon@3x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-0.imageset/animatedLogo-0.png b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-0.imageset/animatedLogo-0.png index ee42954c7..e8d821507 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-0.imageset/animatedLogo-0.png and b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-0.imageset/animatedLogo-0.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-1.imageset/animatedLogo-1.png b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-1.imageset/animatedLogo-1.png index ebdf365db..6c6f15c9c 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-1.imageset/animatedLogo-1.png and b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-1.imageset/animatedLogo-1.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-2.imageset/animatedLogo-2.png b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-2.imageset/animatedLogo-2.png index 1d81e737e..714008e4c 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-2.imageset/animatedLogo-2.png and b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-2.imageset/animatedLogo-2.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-3.imageset/animatedLogo-3.png b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-3.imageset/animatedLogo-3.png index dc6e5e75e..f5e02a9bb 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-3.imageset/animatedLogo-3.png and b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-3.imageset/animatedLogo-3.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-4.imageset/animatedLogo-4.png b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-4.imageset/animatedLogo-4.png index 15418f984..dfe41a330 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-4.imageset/animatedLogo-4.png and b/Riot/Assets/SharedImages.xcassets/AnimatedLogo/animatedLogo-4.imageset/animatedLogo-4.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-1024.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-1024.png index f02cca8bc..ecd3015a3 100644 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-1024.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-1024.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20.png old mode 100644 new mode 100755 index 79f827209..fd7681886 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x-1.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x-1.png old mode 100644 new mode 100755 index 032ddd8ed..839031adb Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x-1.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x-1.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x.png old mode 100644 new mode 100755 index 032ddd8ed..839031adb Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@2x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@3x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@3x.png old mode 100644 new mode 100755 index a3fc645e3..78c68287b Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@3x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-20@3x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29.png old mode 100644 new mode 100755 index 6b0dcba99..979854402 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x-1.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x-1.png old mode 100644 new mode 100755 index dce24e40e..1074e04a9 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x-1.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x-1.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x.png old mode 100644 new mode 100755 index 89b7a290c..1074e04a9 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@2x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@3x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@3x.png old mode 100644 new mode 100755 index 20e272015..b39bcd51f Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@3x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-29@3x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40.png old mode 100644 new mode 100755 index 032ddd8ed..839031adb Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x-1.png old mode 100644 new mode 100755 index c1018364b..6cedc584c Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x-1.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x-1.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x.png old mode 100644 new mode 100755 index f774a1cd6..6cedc584c Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@3x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@3x.png old mode 100644 new mode 100755 index c64f51854..475467f8d Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@3x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-40@3x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@2x.png old mode 100644 new mode 100755 index 8b4ed1d35..475467f8d Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@3x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@3x.png old mode 100644 new mode 100755 index ab174adac..fceef4406 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@3x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76.png old mode 100644 new mode 100755 index b5bbc9f95..99615b99e Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76@2x.png old mode 100644 new mode 100755 index 1a10e395b..fd0ba7c6d Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-83.5@2x.png old mode 100644 new mode 100755 index 629456bf4..844ae7fe4 Binary files a/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-83.5@2x.png and b/Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index b8a0b49b5..02f38588e 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -221,7 +221,7 @@ "room_delete_unsent_messages" = "Изтрий неизпратените съобщения"; "room_event_action_copy" = "Копирай"; "room_event_action_quote" = "Цитирай"; -"room_event_action_redact" = "Изтрий"; +"room_event_action_redact" = "Премахни"; "room_event_action_more" = "Още"; "room_event_action_share" = "Сподели"; "room_event_action_permalink" = "Permalink"; @@ -556,3 +556,70 @@ "room_resource_usage_limit_reached_message_2" = "някои потребители няма да могат да влязат."; "room_resource_usage_limit_reached_message_contact_3" = " за да увеличите този лимит."; "auth_accept_policies" = "Моля, прегледайте и приемете политиките на този сървър:"; +"settings_key_backup" = "ЗАЩИТЕНО ВЪЗСТАНОВЯВАНЕ НА СЪОБЩЕНИЯ"; +"settings_key_backup_info_checking" = "Проверка..."; +"settings_key_backup_info_none" = "Възстановяването на Защитени Съобщения все още не е настроено."; +"settings_key_backup_info_version" = "Версия на резервното копие на ключовете: %@"; +"settings_key_backup_info_algorithm" = "Алгоритъм: %@"; +"settings_key_backup_info_valid" = "Възстановяване на Защитени Съобщения беше настроено за това устройство."; +"settings_key_backup_info_not_valid" = "Възстановяването на Защитени Съобщения не е активно на това устройство."; +"settings_key_backup_info_progress" = "Правене на резервно копие на %@ ключа..."; +"settings_key_backup_info_progress_done" = "Беше направено резервно копие на всички ключове"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "За да използвате Възстановяване на Защитени Съобщения на това устройство, потвърдете %@ сега."; +"settings_key_backup_info_not_trusted_fix_action" = "За да използвате Възстановяване на Защитени Съобщения на това устройство, въведете паролата си или ключа за възстановяване."; +"settings_key_backup_info_trust_signature_unknown" = "Резервното копие има подпис от устройство с идентификатор: %@"; +"settings_key_backup_info_trust_signature_valid" = "Резервното копие има валиден подпис от текущото устройство"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Резервното копие има валиден подпис от %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Резервното копие има валиден подпис от %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Резервното копие има невалиден подпис от %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Резервното копие има невалиден подпис от %@"; +"settings_key_backup_button_create" = "Настройка на Възстановяване на Защитени Съобщения"; +"settings_key_backup_button_restore" = "Възстанови резервното копие"; +"settings_key_backup_button_delete" = "Изтрий резервното копие"; +"settings_key_backup_button_verify" = "Потвърди"; +"settings_key_backup_delete_confirmation_prompt_title" = "Изтриване на резервно копие"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Да се изтрие ли резервното копие на ключовете за шифроване от сървъра? Вече няма да може да използвате ключа за възстановяване за да прочетете шифрованата история на съобщенията"; +"room_does_not_exist" = "%@ не съществува"; +"key_backup_setup_title" = "Възстановяване на Съобщения"; +"key_backup_setup_skip_action" = "Пропусни"; +"key_backup_setup_skip_alert_title" = "Сигурни ли сте?"; +"key_backup_setup_skip_alert_message" = "Може да загубите защитени съобщения, ако излезете от профила или загубите устройството си."; +"key_backup_setup_skip_alert_skip_action" = "Пропусни"; +"key_backup_setup_intro_title" = "Никога не губете шифровани съобщения"; +"key_backup_setup_intro_info" = "Настройте Възстановяване на Защитени Съобщения за да си помогнете, ако излезете от профила или загубите това устройство."; +"key_backup_setup_intro_setup_action" = "Настрой"; +"key_backup_setup_passphrase_info" = "Защитете историята на шифрованите съобщения посредством парола за възстановяване.\n\nЩе Ви е необходима, ако излезете от профила си или загубите достъп до това устройство."; +"key_backup_setup_passphrase_passphrase_title" = "Въвеждане"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Въведете парола"; +"key_backup_setup_passphrase_passphrase_valid" = "Чудесно!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Пробвайте да добавите дума"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Потвърждение"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Въведете паролата отново"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Чудесно!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Паролите не съвпадат"; +"key_backup_setup_passphrase_set_passphrase_action" = "Настрой парола за възстановяване"; +"key_backup_setup_recovery_key_info" = "Направете копие на ключа за възстановяване и го пазете на безопасно място.\n\nАко в бъдеще забравите паролата, може да използвате ключа вместо нея, за да възстановите шифрованите съобщения."; +"key_backup_setup_recovery_key_recovery_key_title" = "Ключ за възстановяване"; +"key_backup_setup_recovery_key_make_copy_action" = "Направи копие"; +"key_backup_setup_recovery_key_made_copy_action" = "Направих копие"; +"key_backup_recover_title" = "Защитени Съобщения"; +"key_backup_recover_empty_backup_title" = "Празно резервно копие"; +"key_backup_recover_empty_backup_message" = "Няма ключ за възстановяване"; +"key_backup_recover_from_passphrase_info" = "Използвайте паролата за възстановяване за да отключите историята на защитените съобщения"; +"key_backup_recover_from_passphrase_passphrase_title" = "Въвеждане"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Въведи парола"; +"key_backup_recover_from_passphrase_recover_action" = "Отключи историята"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не си спомняте паролата за възстановяване? Можете да "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "използвате ключа за възстановяване"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Използвайте ключа за възстановяване за да отключите историята на защитените съобщения"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Въвеждане"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Въведете ключ за възстановяване"; +"key_backup_recover_from_recovery_key_recover_action" = "Отключи историята"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Загубили сте ключа си за възстановяване? Може да създадете нов от Настройки."; +"key_backup_recover_success_info" = "Резервното копие бе възстановено!"; +"key_backup_recover_done_action" = "Готово"; +"key_backup_setup_banner_title_part1" = "Настройка на Възстановяване на Защитени Съобщения"; +"key_backup_setup_banner_title_part2" = " за да не губите шифровани съобщения"; +"key_backup_recover_banner_title_part1" = "Стартирайте Възстановяване на Защитени Съобщения"; +"key_backup_recover_banner_title_part2" = " за да четете шифрованата история на съобщенията на това устройство"; diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index a7ce4c0e7..b9f864f8f 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -543,8 +543,8 @@ "room_replacement_information" = "Dieser Raum wurde ersetzt und ist nicht länger aktiv."; "room_replacement_link" = "Die Konversation wird hier fortgesetzt."; "room_predecessor_information" = "Dieser Raum ist die Fortsetzung einer anderen Konversation."; -"room_predecessor_link" = "Klicke hier um ältere Nachrichten zu sehen."; -"settings_labs_room_members_lazy_loading" = "Raummitglieder verzögert laden"; +"room_predecessor_link" = "Tippe hier um ältere Nachrichten zu sehen."; +"settings_labs_room_members_lazy_loading" = "Raummitglieder nachladen"; "settings_labs_room_members_lazy_loading_error_message" = "Dein Heimserver unterstützt das verzögerte Laden von Raummitgliedern noch nicht. Versuche es später erneut."; "room_event_action_view_decrypted_source" = "Zeige entschlüsselten Quellcode"; "room_recents_server_notice_section" = "SYSTEMBENACHRICHTIGUNGEN"; @@ -557,3 +557,100 @@ "room_resource_usage_limit_reached_message_2" = "einige Benutzer nicht in der Lage sein werden, sich einzuloggen."; "room_resource_usage_limit_reached_message_contact_3" = " um dieses Limit erhöhen zu lassen."; "auth_accept_policies" = "Bitte Regeln dieses Heimservers ansehen und akzeptieren:"; +"settings_key_backup" = "SCHLÜSSEL SICHERHEITSKOPIE"; +"settings_key_backup_info" = "Verschlüsselte Nachrichten sind durch Ende-zu-Ende-Verschlüsselung gesichert. Ausschließlich du und der/die Empfänger besitzen die Schlüssel, um diese Nachrichten zu lesen."; +"settings_key_backup_info_checking" = "Überprüfe..."; +"settings_key_backup_info_none" = "Deine Schlüssel werden von diesem Gerät nicht gesichert."; +"settings_key_backup_info_signout_warning" = "Sichere deine Schlüssel bevor du dich abmeldest, damit du sie nicht verlierst."; +"settings_key_backup_info_version" = "Schlüssel Sicherheitskopie Version: %@"; +"settings_key_backup_info_algorithm" = "Algorithmus"; +"settings_key_backup_info_valid" = "Dieses Gerät sichert deine Schlüssel."; +"settings_key_backup_info_not_valid" = "Dieses Gerät sichert deine Schlüssel nicht."; +"settings_key_backup_info_progress" = "Sichere %@ Schlüssel..."; +"settings_key_backup_info_progress_done" = "Alle Schlüssel wurden gesichert"; +"settings_key_backup_info_trust_signature_unknown" = "Sicherheitskopie hat eine Signatur vom Gerät mit der ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "Sicherheitskopie hat eine gültige Signatur von diesem Gerät"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Sicherheitskopie hat eine gültige Signatur von %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Sicherheitskopie hat eine Signatur von %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Sicherungskopie hat eine ungültige Signatur von %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Sicherungskopie hat eine ungültige Signatur von %@"; +"settings_key_backup_button_create" = "Beginne Wiederherstellung mit Hilfe der Sicherheitskopie"; +"settings_key_backup_button_restore" = "Wiederherstellung mit Hilfe der Sicherheitskopie"; +"settings_key_backup_button_delete" = "Sicherheitskopie löschen"; +"settings_key_backup_button_use" = "Benutze Schlüssel Sicherheitskopie"; +"settings_key_backup_delete_confirmation_prompt_title" = "Sicherheitskopie löschen"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Bist du Sicher? Damit gehen alle verschlüsselten Mitteilungen verloren wenn deine Schlüssel nicht anderweitig richtig gespeichert wurden."; +"room_does_not_exist" = "%@ existiert nicht"; +"key_backup_setup_title" = "Schlüssel Sicherheitskopie"; +"key_backup_setup_skip_alert_title" = "Bist du sicher?"; +"key_backup_setup_skip_alert_message" = "Du könntest deine verschlüsselten Mitteilungen verlieren wenn du dich abmeldest oder dein Gerät verlierst."; +"key_backup_setup_skip_alert_skip_action" = "Überspringen"; +"key_backup_setup_intro_title" = "Verliere niemals verschlüsselte Nachrichten"; +"key_backup_setup_intro_info" = "Nachrichten in verschlüsselten Räumen werden durch Ende-zu-Ende-Verschlüsselung gesichert. Nur Du und der / die Empfänger haben die Schlüssel zum Lesen dieser Nachrichten.\n\nSichere deine Schlüssel sicher, um sie nicht zu verlieren."; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Beginne Schlüsselsicherung zu nutzen"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Benutze Schlüsselsicherung"; +"key_backup_setup_passphrase_title" = "Sichere dein Backup mit einer Passphrase"; +"key_backup_setup_passphrase_info" = "Wir speichern eine verschlüsselte Kopie deiner Schlüssel auf unserem Server. Schütze deine Sicherung mit einer Passphrase, um sie sicher zu halten.\n\nFür maximale Sicherheit sollte sich dies von deinem Kontopasswort unterscheiden."; +"key_backup_setup_passphrase_passphrase_title" = "Gebe"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Passphrase eingeben"; +"key_backup_setup_passphrase_passphrase_valid" = "Gut!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Versuche ein Wort hinzuzufügen"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Bestätigen"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Passphrase bestätigen"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Gut!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrasen müssen übereinstimmen"; +"key_backup_setup_passphrase_set_passphrase_action" = "Setze Passphrase"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Oder sichere deine Sicherung mit einem Wiederherstellungsschlüssel, und sichere sie an einem sicheren Ort."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Erweitert) Mit Wiederherstellungsschlüssel einrichten"; +"key_backup_setup_success_title" = "Erfolgreich!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Deine Schlüssel werden gesichert.\n\nDein Wiederherstellungsschlüssel ist ein Sicherheitsnetzwerk - Du kannst damit den Zugriff auf deine verschlüsselten Nachrichten wiederherstellen, wenn du deine Passphrase vergisst.\n\nBewahre den Wiederherstellungsschlüssel an einem sehr sicheren Ort auf, beispielsweise einem Kennwortmanager (oder einem Tresor)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Speichere den Wiederherstellungsschlüssel"; +"key_backup_setup_success_from_passphrase_done_action" = "Erledigt"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Deine Schlüssel werden gesichert.\n\nErstelle eine Kopie dieses Wiederherstellungsschlüssels und bewahre ihn auf."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Wiederherstellungsschlüssel"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Mache eine Kopie"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Ich fertigte eine Kopie an"; +"key_backup_recover_title" = "Sichere Nachrichten"; +"key_backup_recover_invalid_passphrase_title" = "Falsche Wiederherstellungspassphrase"; +"key_backup_recover_invalid_passphrase" = "Sicherung konnte nicht mit dieser Passphrase entschlüsselt werden: Bitte stelle sicher, dass du die richtige Wiederherstellungspassphrase eingegeben hast."; +"key_backup_recover_invalid_recovery_key_title" = "Wiederherstellungsschlüssel passt nicht"; +"key_backup_recover_invalid_recovery_key" = "Sicherung konnte mit diesem Schlüssel nicht entschlüsselt werden: Bitte stelle sicher, dass du den richtigen Wiederherstellungsschlüssel eingegeben hast."; +"key_backup_recover_from_passphrase_info" = "Nutze deine Wiederherstellungspassphrase um deinen sicheren Chatverlauf zu entschlüsseln"; +"key_backup_recover_from_passphrase_passphrase_title" = "Gebe"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Passphrase eingeben"; +"key_backup_recover_from_passphrase_recover_action" = "Historie entschlüsseln"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Kenne deine Wiederherstellungskennphrase nicht? Du kannst "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "nutze deinen Wiederherstellungsschlüssel"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Nutze deinen Wiederherstellungsschlüssel um deinen sicheren Chatverlauf zu entschlüsseln"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Gebe"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Gebe Wiederherstellungsschlüssel ein"; +"key_backup_recover_from_recovery_key_recover_action" = "Historie entschlüsseln"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Hast du deinen Wiederherstellungsschlüssel verloren? Du kannst in den Einstellungen einen neuen einrichten."; +"key_backup_recover_success_info" = "Sicherung wiederhergestellt"; +"key_backup_recover_done_action" = "Erledigt"; +"key_backup_setup_banner_title" = "Verliere niemals verschlüsselte Nachrichten"; +"key_backup_setup_banner_subtitle" = "Beginne Schlüsselsicherung zu nutzen"; +"key_backup_recover_banner_title" = "Verliere niemals verschlüsselte Nachrichten"; +"key_backup_recover_banner_subtitle" = "Benutze Schlüsselsicherung"; +"sign_out_existing_key_backup_alert_title" = "Bist du sicher, dass du dich abmelden willst?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Abmelden"; +"sign_out_non_existing_key_backup_alert_title" = "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, wenn du dich jetzt abmeldest"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Beginne Schlüsselsicherung zu nutzen"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Ich möchte meine verschlüsselten Nachrichten nicht"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Du wirst deine verschlüsselten Nachrichten verlieren"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, es sei denn, du sicherst deine Schlüssel, bevor du dich abmeldest."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Abmelden"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Sicherungskopie"; +"sign_out_key_backup_in_progress_alert_title" = "Schlüsselsicherung läuft. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ich möchte meine verschlüsselten Nachrichten nicht"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Ich werde warten"; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Neue Schlüsselsicherung"; +"e2e_key_backup_wrong_version" = "Es wurde eine neue Sicherung für einen sicheren Nachrichtenschlüssel erkannt.\n\nWenn dies nicht der Fall war, lege in den Einstellungen eine neue Passphrase fest."; +"e2e_key_backup_wrong_version_button_settings" = "Einstellungen"; +"e2e_key_backup_wrong_version_button_wasme" = "Das war ich"; +"key_backup_setup_intro_manual_export_info" = "(Erweitert)"; +"key_backup_setup_intro_manual_export_action" = "Manueller Schlüssel Export"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9fad99136..301909692 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -254,7 +254,7 @@ "room_delete_unsent_messages" = "Delete unsent messages"; "room_event_action_copy" = "Copy"; "room_event_action_quote" = "Quote"; -"room_event_action_redact" = "Redact"; +"room_event_action_redact" = "Remove"; "room_event_action_more" = "More"; "room_event_action_share" = "Share"; "room_event_action_permalink" = "Permalink"; @@ -278,7 +278,7 @@ "room_replacement_information" = "This room has been replaced and is no longer active."; "room_replacement_link" = "The conversation continues here."; "room_predecessor_information" = "This room is a continuation of another conversation."; -"room_predecessor_link" = "Click here to see older messages."; +"room_predecessor_link" = "Tap here to see older messages."; "room_resource_limit_exceeded_message_contact_1" = " Please "; "room_resource_limit_exceeded_message_contact_2_link" = "contact your service administrator"; "room_resource_limit_exceeded_message_contact_3" = " to continue using this service."; @@ -335,6 +335,7 @@ "settings_flair" = "Show flair where allowed"; "settings_devices" = "DEVICES"; "settings_cryptography" = "CRYPTOGRAPHY"; +"settings_key_backup" = "KEY BACKUP"; "settings_deactivate_account" = "DEACTIVATE ACCOUNT"; "settings_sign_out" = "Sign Out"; @@ -420,6 +421,31 @@ "settings_deactivate_my_account" = "Deactivate my account"; +"settings_key_backup_info" = "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages."; +"settings_key_backup_info_checking" = "Checking..."; +"settings_key_backup_info_none" = "Your keys are not being backed up from this device."; +"settings_key_backup_info_signout_warning" = "Back up your keys before signing out to avoid losing them."; +"settings_key_backup_info_version" = "Key Backup Version: %@"; +"settings_key_backup_info_algorithm" = "Algorithm: %@"; +"settings_key_backup_info_valid" = "This device is backing up your keys."; +"settings_key_backup_info_not_valid" = "This device is not backing up your keys."; +"settings_key_backup_info_progress" = "Backing up %@ keys..."; +"settings_key_backup_info_progress_done" = "All keys backed up"; + +"settings_key_backup_info_trust_signature_unknown" = "Backup has a signature from device with ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "Backup has a valid signature from this device"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Backup has a valid signature from %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Backup has a signature from %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Backup has an invalid signature from %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Backup has an invalid signature from %@"; + +"settings_key_backup_button_create" = "Start using Key Backup"; +"settings_key_backup_button_restore" = "Restore from Backup"; +"settings_key_backup_button_delete" = "Delete Backup"; +"settings_key_backup_button_use" = "Use key backup"; +"settings_key_backup_delete_confirmation_prompt_title" = "Delete Backup"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Are you sure? You will lose your encrypted messages if your keys are not backed up properly."; + // Room Details "room_details_title" = "Room Details"; "room_details_people" = "Members"; @@ -553,6 +579,7 @@ "do_not_ask_again" = "Do not ask again"; "camera_access_not_granted" = "%@ doesn't have permission to use Camera, please change privacy settings"; "large_badge_value_k_format" = "%.1fK"; +"room_does_not_exist" = "%@ does not exist"; // Call "call_incoming_voice_prompt" = "Incoming voice call from %@"; @@ -573,6 +600,12 @@ "e2e_enabling_on_app_update" = "Riot now supports end-to-end encryption but you need to log in again to enable it.\n\nYou can do it now or later from the application settings."; "e2e_need_log_in_again" = "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver.\nThis is a once off; sorry for the inconvenience."; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "New Key Backup"; +"e2e_key_backup_wrong_version" = "A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new passphrase in Settings."; +"e2e_key_backup_wrong_version_button_settings" = "Settings"; +"e2e_key_backup_wrong_version_button_wasme" = "It was me"; + // Bug report "bug_report_title" = "Bug Report"; "bug_report_description" = "Please describe the bug. What did you do? What did you expect to happen? What actually happened?"; @@ -641,3 +674,115 @@ // Re-request confirmation dialog "rerequest_keys_alert_title" = "Request Sent"; "rerequest_keys_alert_message" = "Please launch Riot on another device that can decrypt the message so it can send the keys to this device."; + +// MARK: Key backup setup + +"key_backup_setup_title" = "Key Backup"; + +"key_backup_setup_skip_alert_title" = "Are you sure?"; +"key_backup_setup_skip_alert_message" = "You may lose secure messages if you log out or lose your device."; +"key_backup_setup_skip_alert_skip_action" = "Skip"; + +// Intro + +"key_backup_setup_intro_title" = "Never lose encrypted messages"; +"key_backup_setup_intro_info" = "Messages in encrypted rooms are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.\n\nSecurely back up your keys to avoid losing them."; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Start using Key Backup"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Use Key Backup"; +"key_backup_setup_intro_manual_export_info" = "(Advanced)"; +"key_backup_setup_intro_manual_export_action" = "Manually export keys"; + +// Passphrase + +"key_backup_setup_passphrase_title" = "Secure your backup with a Passphrase"; +"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password."; +"key_backup_setup_passphrase_passphrase_title" = "Enter"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Enter passphrase"; +"key_backup_setup_passphrase_passphrase_valid" = "Great!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Try adding a word"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Confirm"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesn’t match"; +"key_backup_setup_passphrase_set_passphrase_action" = "Set Passphrase"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Or, secure your backup with a Recovery Key, saving it somewhere safe."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Advanced) Set up with Recovery Key"; + +// Success + +"key_backup_setup_success_title" = "Success!"; + +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Save Recovery Key"; +"key_backup_setup_success_from_passphrase_done_action" = "Done"; + +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Recovery Key"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Make a Copy"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "I've made a copy"; + +// MARK: Key backup recover + +"key_backup_recover_title" = "Secure Messages"; + +"key_backup_recover_invalid_passphrase_title" = "Incorrect Recovery Passphrase"; +"key_backup_recover_invalid_passphrase" = "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase."; +"key_backup_recover_invalid_recovery_key_title" = "Recovery Key Mismatch"; +"key_backup_recover_invalid_recovery_key" = "Backup could not be decrypted with this key: please verify that you entered the correct recovery key."; + +// Recover from passphrase + +"key_backup_recover_from_passphrase_info" = "Use your recovery passphrase to unlock your secure message history"; +"key_backup_recover_from_passphrase_passphrase_title" = "Enter"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enter Passphrase"; +"key_backup_recover_from_passphrase_recover_action" = "Unlock History"; + +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Don’t know your recovery passphrase? You can "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "use your recovery key"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; + +// Recover from recovery key + +"key_backup_recover_from_recovery_key_info" = "Use your recovery key to unlock your secure message history"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Enter"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enter Recovery Key"; +"key_backup_recover_from_recovery_key_recover_action" = "Unlock History"; + +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Lost your recovery key? You can set up a new one in settings."; + +// Success + +"key_backup_recover_success_info" = "Backup Restored!"; +"key_backup_recover_done_action" = "Done"; + +// MARK: Key backup banners + +// Setup + +"key_backup_setup_banner_title" = "Never lose encrypted messages"; +"key_backup_setup_banner_subtitle" = "Start using Key Backup"; + +// Recover + +"key_backup_recover_banner_title" = "Never lose encrypted messages"; +"key_backup_recover_banner_subtitle" = "Use Key Backup"; + +// MARK: Sign out warning + +"sign_out_existing_key_backup_alert_title" = "Are you sure you want to sign out?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Sign out"; + +"sign_out_non_existing_key_backup_alert_title" = "You’ll lose access to your encrypted messages if you sign out now"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Start using Key Backup"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "I don't want my encrypted messages"; + +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "You'll lose your encrypted messages"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "You'll lose access to your encrypted messages unless you back up your keys before signing out."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Sign out"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Backup"; + +"sign_out_key_backup_in_progress_alert_title" = "Key backup in progress. If you sign out now you’ll lose access to your encrypted messages."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "I don't want my encrypted messages"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "I'll wait"; diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 9077f5bde..f30779b9f 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -7,7 +7,7 @@ "bug_report_send" = "Bidali"; "room_event_action_copy" = "Kopiatu"; "room_event_action_resend" = "Birbidali"; -"room_event_action_redact" = "Ezabatu"; +"room_event_action_redact" = "Kendu"; "room_event_action_quote" = "Aipua"; "room_event_action_share" = "Partekatu"; "later" = "Geroago"; @@ -39,7 +39,7 @@ "bug_report_description" = "Azaldu akatsa. Zer egin duzu? Zer uste zenuen gertatuko zela? Zer gertatu da benetan?"; "bug_report_logs_description" = "Arazoak aztertzeari begira, bezero honen egunkariak arazte-txosten honekin batera bidaliko dira. Goiko testua besterik ez baduzu bidali nahi, desmarkatu:"; "rage_shake_prompt" = "Telefonoa amorruz astintzen zabiltzala dirudi. Akats baten berri eman nahi duzu?"; -"bug_report_prompt" = "Azken aldian aplikazioa kraskatu da. Kraskatze txostena bidali nahi duzu?"; +"bug_report_prompt" = "Azken aldian aplikazioa kraskatu da. Kraskatze-txostena bidali nahi duzu?"; "auth_register" = "Erregistratu"; // Authentication "auth_login" = "Hasi saioa"; @@ -409,7 +409,7 @@ "no_voip_title" = "Deia jasotzen"; "no_voip" = "%@ zu deitzen ari da baina %@(e)k ez ditu deiak onartzen oraindik.\nJakinarazpen hau ezikusi dezakezu eta deia beste gailu batetik hartu, edo deia baztertu."; // Crash report -"google_analytics_use_prompt" = "%@ hobetzen lagundu nahi duzu kraskatze era erabilera-txosten anonimoak automatikoki bidaliz?"; +"google_analytics_use_prompt" = "%@ hobetzen lagundu nahi duzu kraskatze-txosten era erabilera datu anonimoak automatikoki bidaliz?"; "e2e_need_log_in_again" = "Berriro hasi behar duzu saioa muturretik muturrerako zifratzerako gailu honek gakoak sortzeko eta gako publikoa zure hasiera zerbitzarira bidali behar duzu.\nHau behin bakarrik egin behar duzu, barkatu eragozpenak."; "bug_crash_report_title" = "Kraskatze-txostena"; "bug_crash_report_description" = "Azaldu zer zeunden egiten programa kraskatu aurretik:"; @@ -421,7 +421,7 @@ "room_ongoing_conference_call_with_close" = "Konferentzia deia abian. elkartu %@ edo %@ gisa. %@."; "room_ongoing_conference_call_close" = "Itxi"; "room_conference_call_no_power" = "Baimena behar duzu konferentzia deia kudeatzeko gela honetan"; -"settings_labs_create_conference_with_jitsi" = "Sortu konferentzia deia jitsi erabiliz"; +"settings_labs_create_conference_with_jitsi" = "Sortu konferentzia deia Jitsi erabiliz"; "call_already_displayed" = "Badago de bat abian."; "call_jitsi_error" = "Hutsegitea konferentzia deia elkartzean."; // Widget @@ -545,7 +545,7 @@ "room_replacement_information" = "Gela hau ordeztu da eta ez dago aktibo jada."; "room_replacement_link" = "Elkarrizketak hemen darrai."; "room_predecessor_information" = "Gela hau aurreko elkarrizketa baten jarraipena da."; -"room_predecessor_link" = "Egin klik hemen mezu zaharrak ikusteko."; +"room_predecessor_link" = "Sakatu hemen mezu zaharrak ikusteko."; "settings_labs_room_members_lazy_loading" = "Gelako kideen karga alferra"; "settings_labs_room_members_lazy_loading_error_message" = "Zure hasiera zerbitzariak ez du onartzen gelako kideen karga alferra. Saiatu geroago."; "room_event_action_view_decrypted_source" = "Ikusi deszifratutako iturria"; @@ -559,3 +559,83 @@ "room_resource_usage_limit_reached_message_2" = "erabiltzaile batzuk ezin izango dute saioa hasi."; "room_resource_usage_limit_reached_message_contact_3" = " muga hau areagotzeko."; "auth_accept_policies" = "Irakurri eta onartu hasiera-zerbitzariaren baldintzak:"; +"settings_key_backup" = "GAKOEN BABES-KOPIA"; +"settings_key_backup_info_checking" = "Egiaztatzen..."; +"settings_key_backup_info_none" = "Ez da zure gakoen babes-kopia egiten gailu honetatik."; +"settings_key_backup_info_version" = "Gakoen babes-kopiaren bertsioa: %@"; +"settings_key_backup_info_algorithm" = "Algoritmoa: %@"; +"settings_key_backup_button_restore" = "Berrezarri babes-kopia"; +"settings_key_backup_button_delete" = "Ezabatu babes-kopia"; +"settings_key_backup_button_verify" = "Egiaztatu"; +"settings_key_backup_delete_confirmation_prompt_title" = "Ezabatu babes-kopia"; +"settings_key_backup_info_progress" = "%@ gakoen babes-kopia egiten..."; +"settings_key_backup_info_progress_done" = "Gako guztien babes-kopia egin da"; +"settings_key_backup_info_valid" = "Gailu honek zure gakoen babes-kopia egiten du."; +"settings_key_backup_info_not_valid" = "Gailu honek ez du zure gakoen babes kopia egiten."; +"settings_key_backup_button_create" = "Hasi 'Gakoen babes-kopia' erabiltzen"; +"settings_key_backup_button_use" = "Erabili gakoen babes-kopia"; +"key_backup_setup_passphrase_title" = "Babestu zure babeskopia pasaesaldi batekin"; +"key_backup_setup_passphrase_info" = "Zure gakoen kopia zifratu bat gordeko dugu gure zerbitzarian. Babestu babes-kopia hori pasaesaldi batekin seguru gorde dadin.\n\nSegurtasun hobe baterako, pasaesaldi hau eta zure ohiko pasahitza desberdinak izatea komeni da."; +"key_backup_setup_passphrase_passphrase_title" = "Sartu"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Idatzi pasaesaldia"; +"key_backup_setup_passphrase_passphrase_valid" = "Ongi!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Gehitu hitzen bat"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Baieztatu"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Baieztatu pasaesaldia"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Ongi!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Pasaesaldiak ez datoz bat"; +"key_backup_setup_passphrase_set_passphrase_action" = "Ezarri pasaesaldia"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Edo babestu zure babes-kopia berreskuratze gako batekin, eta beste nonbait gorde."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Aurreratua) Ezarri berreskuratze gakoa"; +"key_backup_setup_success_title" = "Ongi!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Zure gakoen babes-kopia egiten ari da.\n\nZure berreskuratze gakoa badaezpadakoa da, pasaesaldia ahaztuz gero berreskuratze gakoarekin berreskuratu ditzakezu zifratutako mezuak.\n\nGorde zure berreskuratze gakoa toki seguruan, pasahitz kudeatzaile batean esaterako (edo gordailu kutxa batean) ."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Gorde berreskuratze gakoa"; +"key_backup_setup_success_from_passphrase_done_action" = "Egina"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Zure gakoen babes-kopia egiten ari da.\n\nEgin berreskuratze gako honen kopia eta gorde toki seguruan."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Berreskuratze gakoa"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Egin kopia bat"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Kopia bat egin dut"; +"key_backup_recover_title" = "Mezu seguruak"; +"key_backup_recover_invalid_passphrase_title" = "Berreskuratze pasaesaldi okerra"; +"key_backup_recover_invalid_passphrase" = "Ezin izan da babes-kopia deszifratu pasaesaldi honekin: egiaztatu berreskuratze pasaesaldia ondo idatzi duzula."; +"key_backup_recover_invalid_recovery_key_title" = "Berreskuratze gakoak ez datoz bat"; +"key_backup_recover_invalid_recovery_key" = "Ezin izan da babes-kopia deszifratu gako honekin: egiaztatu berreskuratze gako egokia sartu duzula."; +"key_backup_recover_from_passphrase_info" = "Erabili zure berreskuratze pasaesaldia zure mezu seguruen historiala desblokeatzeko"; +"key_backup_recover_from_passphrase_passphrase_title" = "Sartu"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Idatzi pasaesaldia"; +"key_backup_recover_from_passphrase_recover_action" = "Desblokeatu historiala"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Ez duzu berreskuratze pasaesaldia gogoratzen? "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "erabili berreskuratze gakoa"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Erabili berreskuratze gakoa zure mezu seguruen historiala desblokeatzeko"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Sartu"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Sartu berreskuratze gakoa"; +"key_backup_recover_from_recovery_key_recover_action" = "Desblokeatu historiala"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Berreskuratze gakoa galdu duzu? Berri bat sortu dezakezu ezarpenetan."; +"key_backup_recover_success_info" = "Babes-kopia berrezarria!"; +"key_backup_recover_done_action" = "Egina"; +"key_backup_setup_banner_title" = "Ez galdu inoiz zure zifratutako mezuak"; +"key_backup_setup_banner_subtitle" = "Hasi 'Gakoen babes-kopia' erabiltzen"; +"key_backup_recover_banner_title" = "Ez galdu inoiz zifratutako mezuak"; +"key_backup_recover_banner_subtitle" = "Erabili Gakoen babes-kopia"; +"sign_out_existing_key_backup_alert_title" = "Ziur saioa amaitu nahi duzula?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Amaitu saioa"; +"sign_out_non_existing_key_backup_alert_title" = "Zure zifratutako mezuak atzitzeko gaitasuna galduko duzu saioa orain amaitzen baduzu"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Hasi Gakoen babes-kopia erabiltzen"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Ez ditut nire zifratutako mezuak behar"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Zifratutako mezuak galduko dituzu"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Zure zifratutako mezuak atzitzeko gaitasuna galduko duzu ez baduzu zure gakoen babes-kopia bat egiten saioa amaitu aurretik."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Amaitu saioa"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Babes-kopia"; +"sign_out_key_backup_in_progress_alert_title" = "Gakoen babes-kopia abian. Saioa orain amaitzen baduzu zure zifratutako mezuak galduko dituzu."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ez ditut nire zifratutako mezuak behar"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Itxaron egingo dut"; +"settings_key_backup_info" = "Zifratutako mezuak muturretik muturrerako zifratzearen bidez babestuak daude. Zuk eta hartzaileak edo hartzaileek irakurri ditzakezue mezu horiek, beste inork ez."; +"settings_key_backup_info_signout_warning" = "Egin zure gakoen babes-kopia saioa amaitu aurretik galdu nahi ez badituzu."; +"settings_key_backup_info_trust_signature_unknown" = "Babes-kopiak gailu honen sinadura du, ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "Babes-kopiak gailu honen baliozko sinadura bat du"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Gailuak %@ gailuaren baliozko sinadura bat du"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Babes kopiak %@ gailuaren sinadura du"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Babes-kopiak %@ gailuaren baliogabeko sinadura bat du"; diff --git a/Riot/Assets/fi.lproj/InfoPlist.strings b/Riot/Assets/fi.lproj/InfoPlist.strings new file mode 100644 index 000000000..51988aa6f --- /dev/null +++ b/Riot/Assets/fi.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +// Permissions usage explanations +"NSCameraUsageDescription" = "Kameralla otetaan kuvia ja videota sekä soitetaan videopuheluita."; +"NSPhotoLibraryUsageDescription" = "Kuvakirjastoa käytetään kuvien ja videoiden lähetykseen."; +"NSMicrophoneUsageDescription" = "Mikrofonia käytetään puheluissa."; +"NSContactsUsageDescription" = "Jotta Riot ja Matrix yhteyshenkilösi voidaan näyttää, tulee sähköpostosoitteesi ja puhelinnumerosi lähettää identiteettipalvelimelle. New Vector ei tallenna tietoja. Tarvittaessa saat lisätietoja sovelluksen asetussivulta."; diff --git a/Riot/Assets/fi.lproj/Localizable.strings b/Riot/Assets/fi.lproj/Localizable.strings new file mode 100644 index 000000000..3e339a60d --- /dev/null +++ b/Riot/Assets/fi.lproj/Localizable.strings @@ -0,0 +1,2 @@ +/* New message from a specific person, not referencing a room */ +"MSG_FROM_USER" = "Viesti käyttäjältä %@"; diff --git a/Riot/Assets/fi.lproj/Vector.strings b/Riot/Assets/fi.lproj/Vector.strings new file mode 100644 index 000000000..713e12ab2 --- /dev/null +++ b/Riot/Assets/fi.lproj/Vector.strings @@ -0,0 +1,29 @@ +// Titles +"title_home" = "Koti"; +"title_favourites" = "Suosikit"; +"title_people" = "Ihmiset"; +"title_rooms" = "Huoneet"; +"title_groups" = "Yhteisöt"; +"warning" = "Varoitus"; +// Actions +"view" = "Näytä"; +"next" = "Seuraava"; +"back" = "Takaisin"; +"continue" = "Jatka"; +"create" = "Luo"; +"start" = "Aloita"; +"leave" = "Poistu"; +"remove" = "Poista"; +"invite" = "Kutsu"; +"retry" = "Yritä uudelleen"; +"on" = "On"; +"off" = "Off"; +"cancel" = "Peruuta"; +"save" = "Tallenna"; +"join" = "Liity"; +"decline" = "Kiellä"; +"accept" = "Hyväksy"; +"preview" = "Esikatselu"; +"camera" = "Kamera"; +"voice" = "Ääni"; +"video" = "Video"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 844d41785..ae9f29fad 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -543,7 +543,7 @@ "room_replacement_information" = "Ce salon a été remplacé et n'est plus actif."; "room_replacement_link" = "La conversation continue ici."; "room_predecessor_information" = "Ce salon est la suite d'une autre conversation."; -"room_predecessor_link" = "Cliquer ici pour voir les vieux messages."; +"room_predecessor_link" = "Appuyer ici pour voir les vieux messages."; "settings_labs_room_members_lazy_loading" = "Chargement différé des participants des salons"; "settings_labs_room_members_lazy_loading_error_message" = "Votre serveur d'accueil ne prend pas en charge le chargement différé des participants des salons. Réessayez plus tard."; "room_event_action_view_decrypted_source" = "Voir la source déchiffrée"; @@ -557,3 +557,108 @@ "room_resource_usage_limit_reached_message_2" = "quelques utilisateurs ne pourront pas se connecter."; "room_resource_usage_limit_reached_message_contact_3" = " pour augmenter cette limite."; "auth_accept_policies" = "Veuillez lire et accepter les politiques de ce serveur d'accueil :"; +"settings_key_backup" = "SAUVEGARDE DE CLÉS"; +"settings_key_backup_info_checking" = "Vérification…"; +"settings_key_backup_info_none" = "Vos clés ne sont pas sauvegardées depuis cet appareil."; +"settings_key_backup_info_version" = "Version de sauvegarde de clé : %@"; +"settings_key_backup_info_algorithm" = "Algorithme : %@"; +"settings_key_backup_info_valid" = "Cet appareil sauvegarde vos clés."; +"settings_key_backup_info_not_valid" = "Cet appareil ne sauvegarde pas vos clés."; +"settings_key_backup_info_progress" = "Sauvegarde de %@ clés…"; +"settings_key_backup_info_progress_done" = "Toutes les clés ont été sauvegardées"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "Pour utiliser la récupération de messages sécurisée sur cet appareil, vérifiez %@ maintenant."; +"settings_key_backup_info_not_trusted_fix_action" = "Pour utiliser la récupération de messages sécurisée sur cet appareil, fournissez votre phrase de passe ou votre clé de récupération maintenant."; +"settings_key_backup_info_trust_signature_unknown" = "La sauvegarde a une signature de l'appareil ayant pour identifiant : %@"; +"settings_key_backup_info_trust_signature_valid" = "La sauvegarde a une signature valide depuis cet appareil"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "La sauvegarde a une signature valide depuis %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "La sauvegarde a une signature depuis %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "La sauvegarde a une signature non valide depuis %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "La sauvegarde a une signature non valide depuis %@"; +"settings_key_backup_button_create" = "Commencer à utiliser la sauvegarde des clés"; +"settings_key_backup_button_restore" = "Restaurer depuis la sauvegarde"; +"settings_key_backup_button_delete" = "Supprimer la sauvegarde"; +"settings_key_backup_button_verify" = "Vérifier"; +"settings_key_backup_delete_confirmation_prompt_title" = "Supprimer la sauvegarde"; +"settings_key_backup_delete_confirmation_prompt_msg" = "En êtes-vous sûr(e) ? Vous perdrez vos messages chiffrés si vos clés ne sont pas sauvegardées correctement."; +"room_does_not_exist" = "%@ n'existe pas"; +"key_backup_setup_title" = "Sauvegarde de clés"; +"key_backup_setup_skip_action" = "Passer"; +"key_backup_setup_skip_alert_title" = "En êtes-vous certain(e) ?"; +"key_backup_setup_skip_alert_message" = "Vous pourriez perdre vos messages sécurisés si vous vous déconnectez ou si vous perdez votre appareil."; +"key_backup_setup_skip_alert_skip_action" = "Passer"; +"key_backup_setup_intro_title" = "Ne perdez jamais vos messages chiffrés"; +"key_backup_setup_intro_info" = "Les messages des salons chiffrés sont sécurisés avec un chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) avez les clés pour lire ces messages.\n\nSauvegardez vos clés de façon sécurisée pour éviter de les perdre."; +"key_backup_setup_intro_setup_action" = "Configurer"; +"key_backup_setup_passphrase_info" = "Nous conserverons une copie chiffrée de vos clés sur notre serveur. Protégez votre sauvegarde avec une phrase de passe pour qu'elle soit sécurisée.\n\nPour une sécurité maximale, elle devrait être différente du mot de passe de votre compte."; +"key_backup_setup_passphrase_passphrase_title" = "Saisir"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Saisir la phrase de passe"; +"key_backup_setup_passphrase_passphrase_valid" = "Super !"; +"key_backup_setup_passphrase_passphrase_invalid" = "Essayez d'ajouter un mot"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Confirmer"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirmer la phrase de passe"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Super !"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Les phrases de passe ne correspondent pas"; +"key_backup_setup_passphrase_set_passphrase_action" = "Définir la phrase de passe"; +"key_backup_setup_recovery_key_info" = "Faites une copie de cette clé de récupération et conservez-la en lieu sûr.\n\nPar mesure de précaution, vous pouvez l'utiliser pour restaurer votre historique de messages chiffrés si vous oubliez votre phrase de passe de récupération."; +"key_backup_setup_recovery_key_recovery_key_title" = "Clé de récupération"; +"key_backup_setup_recovery_key_make_copy_action" = "En faire une copie"; +"key_backup_setup_recovery_key_made_copy_action" = "J'en ai fait une copie"; +"key_backup_recover_title" = "Messages sécurisés"; +"key_backup_recover_empty_backup_title" = "Sauvegarde vide"; +"key_backup_recover_empty_backup_message" = "Il n'y a aucune clé à restaurer"; +"key_backup_recover_from_passphrase_info" = "Utilisez votre phrase de passe de récupération pour déverrouiller votre historique de messages sécurisés"; +"key_backup_recover_from_passphrase_passphrase_title" = "Saisir"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Saisir la phrase de passe"; +"key_backup_recover_from_passphrase_recover_action" = "Déverrouiller l'historique"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Vous ne connaissez pas votre phrase de passe de récupération ? Vous pouvez "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "utiliser votre clé de récupération"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Utiliser votre clé de récupération pour déverrouiller l'historique de vos messages sécurisés"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Saisir"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Saisir la clé de récupération"; +"key_backup_recover_from_recovery_key_recover_action" = "Déverrouiller l'historique"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Vous avez perdu votre clé de récupération ? Vous pouvez en configurer une autre dans les réglages."; +"key_backup_recover_success_info" = "Sauvegarde restaurée !"; +"key_backup_recover_done_action" = "Effectué"; +"key_backup_setup_banner_title_part1" = "Configurer la récupération de messages sécurisée"; +"key_backup_setup_banner_title_part2" = " pour ne jamais perdre vos messages chiffrés"; +"key_backup_recover_banner_title_part1" = "Lancer la récupération de messages sécurisée"; +"key_backup_recover_banner_title_part2" = " pour lire l'historique des messages chiffrés sur cet appareil"; +"settings_key_backup_info" = "Les messages chiffrés sont sécurisés avec un chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) avez les clés pour lire ces messages."; +"settings_key_backup_info_signout_warning" = "Sauvegardez vos clés avant de vous déconnecter pour éviter de les perdre."; +"settings_key_backup_button_use" = "Utiliser la sauvegarde de clés"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Commencer à utiliser la sauvegarde de clés"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Utiliser la sauvegarde de clés"; +"key_backup_setup_passphrase_title" = "Protégez votre sauvegarde avec une phrase de passe"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Sinon, protégez votre sauvegarde avec une clé de récupération, en la conservant dans un endroit sûr."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Avancé) Configurer avec la clé de récupération"; +"key_backup_setup_success_title" = "Terminé !"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Vos clés sont en cours de sauvegarde.\n\nVotre clé de récupération est une mesure de précaution. Vous pouvez l'utiliser pour restaurer l'accès à vos messages chiffrés si vous oubliez votre phrase de passe.\n\nConservez votre clé de récupération dans un lieu très sûr, comme un gestionnaire de mots de passe (ou un coffre-fort)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Sauvegarder la clé de récupération"; +"key_backup_setup_success_from_passphrase_done_action" = "Terminé"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Vos clés sont en cours de sauvegarde.\n\nFaites une copie de cette clé de récupération et conservez-la en lieu sûr."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Clé de récupération"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "En faire une copie"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "J'ai fait une copie"; +"key_backup_recover_invalid_passphrase_title" = "Phrase de passe de récupération incorrecte"; +"key_backup_recover_invalid_passphrase" = "La sauvegarde n'a pas pu être déchiffrée avec cette phrase de passe : vérifiez que vous avez saisi la bonne phrase de passe de récupération."; +"key_backup_recover_invalid_recovery_key_title" = "La clé de récupération ne correspond pas"; +"key_backup_recover_invalid_recovery_key" = "La sauvegarde n'a pas pu être déchiffrée avec cette clé : vérifiez que vous avez saisi la bonne clé de récupération."; +"key_backup_setup_banner_title" = "Ne perdez jamais vos messages chiffrés"; +"key_backup_setup_banner_subtitle" = "Commencez à utiliser la sauvegarde de clés"; +"key_backup_recover_banner_title" = "Ne perdez jamais vos messages chiffrés"; +"key_backup_recover_banner_subtitle" = "Utilisez la sauvegarde de clés"; +"sign_out_existing_key_backup_alert_title" = "Voulez-vous vraiment vous déconnecter ?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Se déconnecter"; +"sign_out_non_existing_key_backup_alert_title" = "Vous n'aurez plus accès à vos messages chiffrés si vous vous déconnectez maintenant"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Commencer à utiliser la sauvegarde de clés"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Je ne veux pas de mes messages chiffrés"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Vous perdrez vos messages chiffrés"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Vous n'aurez plus accès à vos messages chiffrés sauf si vous sauvegardez vos clés avant de vous déconnecter."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Se déconnecter"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Sauvegarde"; +"sign_out_key_backup_in_progress_alert_title" = "Sauvegarde de clés en cours. Si vous vous déconnectez maintenant vous n'aurez plus accès à vos messages chiffrés."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Je ne veux plus de mes messages chiffrés"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Je vais patienter"; diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index c73c95ff1..7a403aa24 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -222,7 +222,7 @@ "room_delete_unsent_messages" = "Elküldetlen üzenetek törlése"; "room_event_action_copy" = "Másol"; "room_event_action_quote" = "Idéz"; -"room_event_action_redact" = "Kitakar"; +"room_event_action_redact" = "Töröl"; "room_event_action_more" = "További"; "room_event_action_share" = "Megosztás"; "room_event_action_permalink" = "Állandó hivatkozás"; @@ -246,7 +246,7 @@ "room_replacement_information" = "Ezt a szobát lecserélték és már nem aktív."; "room_replacement_link" = "A beszélgetés itt folytatódik."; "room_predecessor_information" = "Ez a szoba egy másik megbeszélés folytatása."; -"room_predecessor_link" = "Kattints ide a régi üzenetek megtekintéséhez."; +"room_predecessor_link" = "Koppints ide a régi üzenetek megtekintéséhez."; // Unknown devices "unknown_devices_alert_title" = "A szoba ismeretlen eszközöket tartalmaz"; "unknown_devices_alert" = "A szobában vannak ellenőrizetlen eszközök.\nEz azt jelenti, hogy nem garantálható, hogy az eszköz azé akiének mondja magát.\nJavasoljuk, hogy menj végig az ellenőrzési folyamaton mielőtt folytatod, de ha azt szeretnéd újraküldheted az üzenetet ellenőrzés nélkül is."; @@ -562,3 +562,115 @@ "room_resource_usage_limit_reached_message_2" = "néhány felhasználó nem tud majd bejelentkezni."; "room_resource_usage_limit_reached_message_contact_3" = " hogy korlátot megemeljék."; "auth_accept_policies" = "A Matrix szerver felhasználási feltételeit kérlek nézd át és fogadd el:"; +"settings_key_backup" = "KULCS MENTÉS"; +"settings_key_backup_info_checking" = "Ellenőrzés..."; +"settings_key_backup_info_none" = "A kulcsaid nem kerülnek elmentésre erről az eszközről."; +"settings_key_backup_info_version" = "Kulcs mentés verzió: %@"; +"settings_key_backup_info_algorithm" = "Algoritmus: %@"; +"settings_key_backup_info_valid" = "Ez az eszköz elmenti a kulcsaidat."; +"settings_key_backup_info_not_valid" = "Ez az eszköz nem menti el a kulcsaidat."; +"settings_key_backup_info_progress" = "%@ kulcsok mentése..."; +"settings_key_backup_info_progress_done" = "Minden kulcs elmentve"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "A Biztonságos Üzenet Visszaállítás használatához ellenőrizd ezt: %@."; +"settings_key_backup_info_not_trusted_fix_action" = "Ha a Biztonságos Üzenete Visszaállítást ezen az eszközön használni szeretnéd, akkor most add meg a jelmondatodat vagy a visszaállítási kulcsot."; +"settings_key_backup_info_trust_signature_unknown" = "A mentés aláírással rendelkezik az alábbi eszköz azonosítóval: %@"; +"settings_key_backup_info_trust_signature_valid" = "A mentés érvényes aláírással rendelkezik ettől az eszköztől"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "A mentés érvényes aláírással rendelkezik ettől: %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "A mentés rendelkezik aláírással ettől: %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "A mentésnek érvénytelen aláírása van ettől: %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "A mentésnek érvénytelen aláírása van ettől: %@"; +"settings_key_backup_button_create" = "Kulcs mentés használata"; +"settings_key_backup_button_restore" = "Visszaállítás mentésből"; +"settings_key_backup_button_delete" = "Mentés törlése"; +"settings_key_backup_button_verify" = "Ellenőrzés"; +"settings_key_backup_delete_confirmation_prompt_title" = "Mentés törlése"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Biztos vagy benne? Elveszted a titkosított üzeneteidet ha a kulcsok nincsenek megfelelően elmentve."; +"room_does_not_exist" = "%@ nem létezik"; +"key_backup_setup_title" = "Kulcs Mentés"; +"key_backup_setup_skip_action" = "Kihagy"; +"key_backup_setup_skip_alert_title" = "Biztos?"; +"key_backup_setup_skip_alert_message" = "Ha kilépsz vagy elhagyod az eszközöd nem férsz majd hozzá a biztonságos üzeneteidhez."; +"key_backup_setup_skip_alert_skip_action" = "Kihagy"; +"key_backup_setup_intro_title" = "Soha ne veszítsd el a titkosított üzeneteidet"; +"key_backup_setup_intro_info" = "Titkosított szobákban az üzenetek végponttól-végpontig vannak titkosítva. Csak te és a címzettek rendelkeznek a kulcsokkal az üzenetek visszafejtéséhez.\n\nMentsd el megfelelően a kulcsaidat, hogy ne veszítsd el őket."; +"key_backup_setup_intro_setup_action" = "Beállítás"; +"key_backup_setup_passphrase_info" = "A kulcsaid titkosított másolatát elmentjük a szerverünkre. Védd a mentést jelmondattal.\n\nA maximális biztonság érdekében ez a jelmondat különbözzön a felhasználói fiókhoz használttól."; +"key_backup_setup_passphrase_passphrase_title" = "Bead"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Jelmondat bevitele"; +"key_backup_setup_passphrase_passphrase_valid" = "Szuper!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Próbálj hozzáadni egy szót"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Megerősítés"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Jelmondat megerősítése"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Szuper!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "A jelmondatok nem egyeznek meg"; +"key_backup_setup_passphrase_set_passphrase_action" = "Jelmondat beállítása"; +"key_backup_setup_recovery_key_info" = "Erről a Visszaállítási Kulcsról készíts egy másolatot és őrizd meg biztonságos helyen.\n\nEz egy további biztosíték, amivel újra hozzáférhetsz a titkosított üzeneteidhez ha elfelejtenéd a Visszaállítási Jelmondatot."; +"key_backup_setup_recovery_key_recovery_key_title" = "Visszaállítási Kulcs"; +"key_backup_setup_recovery_key_make_copy_action" = "Másolat készítése"; +"key_backup_setup_recovery_key_made_copy_action" = "Készítettem másolatot"; +"key_backup_recover_title" = "Biztonságos Üzenetek"; +"key_backup_recover_empty_backup_title" = "Üres mentés"; +"key_backup_recover_empty_backup_message" = "Nincs mentendő kulcs"; +"key_backup_recover_from_passphrase_info" = "Használd a visszaállítási jelmondatot, hogy újra hozzáférj a biztonságos üzeneteidhez"; +"key_backup_recover_from_passphrase_passphrase_title" = "Bevitel"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Jelmondat bevitele"; +"key_backup_recover_from_passphrase_recover_action" = "Újra hozzáférés a régi üzenetekhez"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Nem emlékszel a visszaállítási jelmondatodra? Használhatod a "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "visszaállítási kulcsot"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Használd a visszaállítási kulcsod, hogy újra hozzáférhess a biztonságos üzeneteidhez"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Bevitel"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Visszaállítási Kulcs megadása"; +"key_backup_recover_from_recovery_key_recover_action" = "Hozzáférés a régi üzenetekhez"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Elvesztetted a visszaállítási kulcsod? A beállításokban készíthetsz egy újat."; +"key_backup_recover_success_info" = "Mentés visszaállítva!"; +"key_backup_recover_done_action" = "Kész"; +"key_backup_setup_banner_title_part1" = "Biztonságos Üzenet Visszaállítás beállítása"; +"key_backup_setup_banner_title_part2" = " , hogy sose veszítsd el a titkosított üzeneteidet"; +"key_backup_recover_banner_title_part1" = "Biztonságos Üzenet Visszaállítás futtatása"; +"key_backup_recover_banner_title_part2" = " , hogy elolvashasd a titkosított üzeneteidet ezen az eszközön"; +"settings_key_backup_info" = "Titkosított üzenetek végponttól-végpontig vannak titkosítva. Csak te és a címzettek rendelkeznek a visszafejtéshez szükséges kulcsokkal."; +"settings_key_backup_info_signout_warning" = "Mentsd el a kulcsaidat a kilépés előtt, hogy ne veszítsd el őket."; +"settings_key_backup_button_use" = "Kulcs mentés használata"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Kulcs mentés használata"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Használd a Kulcs mentést"; +"key_backup_setup_passphrase_title" = "Védd a mentést jelmondattal"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Vagy védd a mentést egy Visszaállítási Kulccsal és tedd el egy biztonságos helyre."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Haladó) Visszaállítási Kulcs beállítása"; +"key_backup_setup_success_title" = "Sikerült!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "A kulcsaid elmentésre kerülnek.\n\nA Visszaállítási Kulcs egy olyan biztonsági elem amivel akkor is visszaállíthatod a hozzáférésed a titkosított üzenetekhez ha a jelmondatot elfelejted.\n\nA Visszaállítási kulcsot biztonságos helyen tárold, mint pl. egy jelszókezelőben (vagy széfben)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Visszaállítási Kulcs beállítása"; +"key_backup_setup_success_from_passphrase_done_action" = "Kész"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "A kulcsaid elmentésre kerülnek.\n\nMásold ki a Visszaállítási Kulcsot és tárold biztonságos helyen."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Visszaállítási Kulcs"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Készíts másolatot"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Készítettem másolatot"; +"key_backup_recover_invalid_passphrase_title" = "Helytelen Visszaállítási Jelmondat"; +"key_backup_recover_invalid_passphrase" = "A mentést nem lehet visszafejteni ezzel a jelmondattal: kérlek ellenőrizd, hogy a megfelelő visszaállítási jelmondatot adtad-e meg."; +"key_backup_recover_invalid_recovery_key_title" = "A Visszaállítási Kulcsok nem egyeznek meg"; +"key_backup_recover_invalid_recovery_key" = "A mentést nem lehet ezzel a kulccsal visszafejteni: kérlek ellenőrizd, hogy a megfelelő kulcsot adtad-e meg."; +"key_backup_setup_banner_title" = "Soha ne veszítsd el a titkosított üzeneteidet"; +"key_backup_setup_banner_subtitle" = "Kulcs mentés használata"; +"key_backup_recover_banner_title" = "Soha ne veszítsd el a titkosított üzeneteidet"; +"key_backup_recover_banner_subtitle" = "Kulcs mentés használata"; +"sign_out_existing_key_backup_alert_title" = "Biztos, hogy ki szeretnél jelentkezni?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Kijelentkezés"; +"sign_out_non_existing_key_backup_alert_title" = "Elveszted a hozzáférést a titkosított üzeneteidhez ha most kijelentkezel"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Kulcs mentés használatának megkezdése"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Nincs szükségem a titkosított üzeneteimre"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Elveszted minden titkosított üzenetedet"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Elveszted minden titkosított üzenetedet ha nem mented el a kulcsaidat kijelentkezés előtt."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Kijelentkezés"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Mentés"; +"sign_out_key_backup_in_progress_alert_title" = "Kulcsok mentése folyamatban van. Ha most kijelentkezel akkor elveszted a titkosított üzeneteidet."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Nincs szükségem a titkosított üzeneteimre"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Inkább várok"; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Új Kulcs Mentés"; +"e2e_key_backup_wrong_version" = "Új biztonságos üzenet kulcs mentést észleltünk.\n\nHa ez nem te voltál akkor állíts be egy új jelmondatot a Beállításokban."; +"e2e_key_backup_wrong_version_button_settings" = "Beállítások"; +"e2e_key_backup_wrong_version_button_wasme" = "Én voltam"; +"key_backup_setup_intro_manual_export_info" = "(Haladó)"; +"key_backup_setup_intro_manual_export_action" = "Kulcsok kimentése kézzel"; diff --git a/Riot/Assets/pl.lproj/Localizable.strings b/Riot/Assets/pl.lproj/Localizable.strings index 03f3655bb..77a3fa5b0 100644 --- a/Riot/Assets/pl.lproj/Localizable.strings +++ b/Riot/Assets/pl.lproj/Localizable.strings @@ -22,3 +22,17 @@ "MSGS_FROM_TWO_PLUS_USERS" = "%@ nowych wiadomości od %@, %@ i innych"; /* Look, stuff's happened, alright? Just open the app. */ "MSGS_IN_TWO_PLUS_ROOMS" = "%@ nowych wiadomości w %@, %@ i innych"; +/* New action message from a specific person, not referencing a room. */ +"IMAGE_FROM_USER" = "%@ wysłał(a) Tobie zdjęcie %@"; +/* New action message from a specific person in a named room. */ +"IMAGE_FROM_USER_IN_ROOM" = "%@ wysłał(a) Tobie zdjęcie %@ w %@"; +/* Multiple unread messages from a specific person, not referencing a room */ +"MSGS_FROM_USER" = "%@ nowych wiadomości w %@"; +/* Multiple messages in two rooms */ +"MSGS_IN_TWO_ROOMS" = "%@ nowych wiadomości w %@ i %@"; +/* A user has invited you to a named room */ +"USER_INVITE_TO_NAMED_ROOM" = "%@ zaprosił(a) Ciebie do %@"; +/* A user has invited you to an (unamed) group chat */ +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy grupowej"; +/* A user has invited you to a chat */ +"USER_INVITE_TO_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy"; diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings index 234ae0357..86071d940 100644 --- a/Riot/Assets/pl.lproj/Vector.strings +++ b/Riot/Assets/pl.lproj/Vector.strings @@ -487,3 +487,4 @@ // Media picker "media_picker_library" = "Biblioteka"; "large_badge_value_k_format" = "%.1fK"; +"bug_crash_report_title" = "Raport o awarii"; diff --git a/Riot/Assets/pt_BR.lproj/InfoPlist.strings b/Riot/Assets/pt_BR.lproj/InfoPlist.strings index da30fcefa..60e4b10f3 100644 --- a/Riot/Assets/pt_BR.lproj/InfoPlist.strings +++ b/Riot/Assets/pt_BR.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "NSCameraUsageDescription" = "A câmera será usada para tomar fotos e vídeos, e também para a realização de chamadas de vídeo."; "NSPhotoLibraryUsageDescription" = "A galeria de fotos é usada para o envio de fotos e vídeos."; "NSMicrophoneUsageDescription" = "O microfone é usado para gravar vídeos e fazer chamadas, tanto de áudio como de vídeo."; -"NSContactsUsageDescription" = "O cadernos de contatos é usado para fazer busca de usuárias/os no Riot a partir do e-mail ou do número de telefone."; +"NSContactsUsageDescription" = "Para mostrar a você quais de seus contatos já estão usando o Riot ou o Matrix, podemos enviar os endereços de e-mail e números de telefone em seu catálogo de endereços para o Matrix Identity Server. A New Vector não armazena esses dados ou usa-os para qualquer outra finalidade. Para mais informações, consulte a página da política de privacidade nas configurações do aplicativo."; diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 32eea684f..81310fb58 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -158,4 +158,401 @@ "room_creation_title" = "Nova Sala"; "room_creation_account" = "Conta"; // Room recents -"room_recents_directory_section" = "LISTA PÚBLICA DE SALAS"; +"room_recents_directory_section" = "DIRETÓRIO DE SALAS"; +"directory_search_results_title" = "Procurar nos resultados do diretório"; +"directory_search_fail" = "Falha ao buscar dados"; +"auth_accept_policies" = "Por favor, revise e aceite as políticas deste homeserver:"; +"room_recents_server_notice_section" = "ALERTAS DO SISTEMA"; +"room_participants_remove_third_party_invite_msg" = "Remover convite de terceiros ainda não é suportado até que a API exista"; +"room_participants_invite_prompt_title" = "Confirmação"; +"room_participants_invite_prompt_msg" = "Tem certeza de que deseja convidar %@ para este chat?"; +"room_participants_filter_room_members" = "Filtrar membros da sala"; +"room_participants_invite_another_user" = "Pesquisar / convidar por ID do usuário, nome ou e-mail"; +"room_participants_invite_malformed_id_title" = "Erro no Convite"; +"room_participants_invited_section" = "CONVIDADO"; +"room_participants_online" = "Online"; +"room_participants_offline" = "Offline"; +"room_participants_unknown" = "Desconhecido"; +"room_participants_idle" = "Ocioso"; +"room_participants_now" = "agora"; +"room_participants_ago" = "atrás"; +"room_participants_action_section_admin_tools" = "Ferramentas Administrativas"; +"room_participants_action_section_direct_chats" = "bate-papos diretos"; +"room_participants_action_section_devices" = "Dispositivos"; +"room_participants_action_section_other" = "Outros"; +"room_participants_action_invite" = "Convidar"; +"room_participants_action_leave" = "Sair desta sala"; +"room_participants_action_remove" = "Remover desta sala"; +"room_participants_action_ban" = "Bloquear desta sala"; +"room_participants_action_unban" = "Desbloquear"; +"room_participants_action_ignore" = "Esconder todas as mensagens deste usuário"; +"room_participants_action_unignore" = "Mostrar todas as mensagens deste usuário"; +"room_participants_invite_malformed_id" = "ID malformado. Deve ser utlizado um endereço de e-mail ou um Matrix ID, como \"@localpart:domain\""; +"room_participants_action_set_default_power_level" = "Redefinir para usuário normal"; +"room_participants_action_set_moderator" = "Faça moderador"; +"room_participants_action_set_admin" = "Faça administrador"; +"room_participants_action_start_new_chat" = "Iniciar um novo bate-papo"; +"room_participants_action_start_voice_call" = "Iniciar chamada de voz"; +"room_participants_action_start_video_call" = "Iniciar chamada de vídeo"; +"room_participants_action_mention" = "Menção"; +// Chat +"room_jump_to_first_unread" = "Ir para a primeira mensagem não lida"; +"room_new_message_notification" = "%d nova mensagem"; +"room_new_messages_notification" = "%d novas mensagens"; +"room_one_user_is_typing" = "%@ está digitando…"; +"room_two_users_are_typing" = "%@ & %@ estão digitando…"; +"room_many_users_are_typing" = "%@, %@ e outros estão digitando…"; +"room_message_placeholder" = "Enviar uma mensagem (não criptografada)…"; +"room_message_reply_to_placeholder" = "Enviar uma resposta (não criptografada)…"; +"room_do_not_have_permission_to_post" = "Você não tem permissão para postar nesta sala"; +"encrypted_room_message_placeholder" = "Enviar uma mensagem criptografada…"; +"encrypted_room_message_reply_to_placeholder" = "Enviar uma resposta criptografada…"; +"room_message_short_placeholder" = "Enviar uma mensagem…"; +"room_message_reply_to_short_placeholder" = "Enviar uma resposta…"; +"room_offline_notification" = "A conectividade com o servidor foi perdida."; +"room_unsent_messages_notification" = "Mensagens não enviadas. %@ ou %@ agora?"; +"room_unsent_messages_unknown_devices_notification" = "Mensagem não enviada devido a dispositivos desconhecidos presentes. %@ ou %@ agora?"; +"room_ongoing_conference_call" = "Teleconferência em andamento. Junte-se como %@ ou %@."; +"room_ongoing_conference_call_with_close" = "Teleconferência em andamento. Junte-se como %@ ou %@. %@ isto."; +"room_ongoing_conference_call_close" = "Fechar"; +"room_conference_call_no_power" = "Você precisa de permissão para gerenciar a teleconferência nesta sala"; +"room_prompt_resend" = "Reenviar tudo"; +"room_prompt_cancel" = "Cancelar todas"; +"room_resend_unsent_messages" = "Reenviar mensagens não enviadas"; +"room_delete_unsent_messages" = "Excluir mensagens não enviadas"; +"room_event_action_copy" = "Copiar"; +"room_event_action_quote" = "Citar"; +"room_event_action_redact" = "Redigir"; +"room_event_action_more" = "Mais"; +"room_event_action_share" = "Compartilhar"; +"room_event_action_permalink" = "Link permanente"; +"room_event_action_view_source" = "Ver fonte"; +"room_event_action_view_decrypted_source" = "Ver fonte descriptografado"; +"room_event_action_report" = "Denunciar conteúdo"; +"room_event_action_report_prompt_reason" = "Razão para denunciar este conteúdo"; +"room_event_action_kick_prompt_reason" = "Razão para chutar este usuário"; +"room_event_action_ban_prompt_reason" = "Razão para banir este usuário"; +"room_event_action_report_prompt_ignore_user" = "Você quer esconder todas as mensagens deste usuário?"; +"room_event_action_save" = "Salvar"; +"room_event_action_resend" = "Reenviar"; +"room_event_action_delete" = "Excluir"; +"room_event_action_cancel_send" = "Cancelar envio"; +"room_event_action_cancel_download" = "Cancelar download"; +"room_event_action_view_encryption" = "Informação criptografada"; +"room_warning_about_encryption" = "A criptografia de ponta a ponta está na versão beta e pode não ser confiável.\n\nVocê ainda não deve confiar nele para proteger os dados.\n\nOs dispositivos ainda não poderão descriptografar o histórico antes de entrarem na sala.\n\nAs mensagens criptografadas não estarão visíveis em clientes que ainda não implementam a criptografia."; +"room_event_failed_to_send" = "Falha ao enviar"; +"room_action_send_photo_or_video" = "Enviar foto ou vídeo"; +"room_action_send_sticker" = "Enviar sticker"; +"room_replacement_information" = "Esta sala foi substituída e não está mais ativa."; +"room_replacement_link" = "A conversa continua aqui."; +"room_predecessor_information" = "Esta sala é uma continuação de outra conversa."; +"room_predecessor_link" = "Clique aqui para ver as mensagens mais antigas."; +"room_resource_limit_exceeded_message_contact_1" = " Por favor "; +"room_resource_limit_exceeded_message_contact_2_link" = "entre em contato com o administrador do serviço"; +"room_resource_limit_exceeded_message_contact_3" = " para continuar usando este serviço."; +"room_resource_usage_limit_reached_message_1_default" = "Este homeserver ultrapassou um dos seus limites de recursos então "; +"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Esse homeserver atingiu seu limite de usuário ativo mensal então "; +"room_resource_usage_limit_reached_message_2" = "alguns usuários não poderão efetuar login."; +"room_resource_usage_limit_reached_message_contact_3" = " para ter este limite aumentado."; +// Unknown devices +"unknown_devices_alert_title" = "A sala contém dispositivos desconhecidos"; +"unknown_devices_send_anyway" = "Enviar mesmo assim"; +"unknown_devices_call_anyway" = "Ligar mesmo assim"; +"unknown_devices_answer_anyway" = "Responder mesmo assim"; +"unknown_devices_verify" = "Verificar…"; +"unknown_devices_title" = "Dispositivos desconhecidos"; +// Room Title +"room_title_new_room" = "Nova sala"; +"room_title_multiple_active_members" = "%@/%@ membros ativos"; +"room_title_one_active_member" = "%@/%@ membro ativo"; +"room_title_invite_members" = "Convidar membros"; +"room_title_members" = "%@ membros"; +"room_title_one_member" = "1 membro"; +// Room Preview +"room_preview_invitation_format" = "Você foi convidado a participar desta sala por %@"; +"unknown_devices_alert" = "Esta sala contém dispositivos desconhecidos que não foram verificados.\nIsso significa que não há garantia de que os dispositivos pertencem aos usuários que eles reivindicam.\nRecomendamos que você passe pelo processo de verificação de cada dispositivo antes de continuar, mas é possível reenviar a mensagem sem verificar se preferir."; +"room_preview_subtitle" = "Esta é uma prévia desta sala. Interações de sala foram desativadas."; +"room_preview_unlinked_email_warning" = "Este convite foi enviado para %@, que não está associado a esta conta. Você pode querer fazer login com uma conta diferente ou adicionar este e-mail a essa conta."; +"room_preview_try_join_an_unknown_room" = "Você está tentando acessar %@. Você gostaria de entrar para participar da discussão?"; +"room_preview_try_join_an_unknown_room_default" = "uma sala"; +// Settings +"settings_title" = "Configurações"; +"account_logout_all" = "Sair de todas as contas"; +"settings_config_no_build_info" = "Nenhuma informação da build"; +"settings_mark_all_as_read" = "Marcar todas as mensagens como lidas"; +"settings_report_bug" = "Relatar erro"; +"settings_config_home_server" = "Home server é %@"; +"settings_config_identity_server" = "Servidor de Identidades é %@"; +"settings_config_user_id" = "Logado como %@"; +"settings_user_settings" = "CONFIGURAÇÕES DE USUÁRIO"; +"settings_notifications_settings" = "CONFIGURAÇÕES DE NOTIFICAÇÕES"; +"settings_calls_settings" = "CHAMADAS"; +"settings_user_interface" = "INTERFACE DO USUÁRIO"; +"settings_ignored_users" = "USUÁRIOS IGNORADOS"; +"settings_contacts" = "CONTATOS LOCAIS"; +"settings_advanced" = "AVANÇADO"; +"settings_other" = "OUTROS"; +"settings_labs" = "LABORATÓRIO"; +"settings_flair" = "Mostrar insígnias onde permitido"; +"settings_devices" = "DISPOSITIVOS"; +"settings_cryptography" = "CRIPTOGRAFIA"; +"settings_deactivate_account" = "DESATIVAR A CONTA"; +"settings_sign_out" = "Sair"; +"settings_sign_out_confirmation" = "Você tem certeza?"; +"settings_sign_out_e2e_warn" = "Você perderá suas chaves de criptografia de ponta a ponta. Isso significa que você não poderá mais ler mensagens antigas em salas criptografadas neste dispositivo."; +"settings_profile_picture" = "Imagem de Perfil"; +// Room Details +"room_details_title" = "Detalhes da sala"; +"settings_first_name" = "Primeiro nome"; +"settings_surname" = "Sobrenome"; +"settings_remove_prompt_title" = "Confirmação"; +"settings_remove_email_prompt_msg" = "Tem certeza de que deseja remover o endereço de e-mail %@?"; +"settings_remove_phone_prompt_msg" = "Tem certeza de que deseja remover o número de telefone %@?"; +"settings_email_address" = "E-mail"; +"settings_email_address_placeholder" = "Insira o seu endereço de e-mail"; +"settings_add_email_address" = "Adicionar endereço de e-mail"; +"settings_phone_number" = "Telefone"; +"settings_add_phone_number" = "Adicionar número de telefone"; +"settings_night_mode" = "Modo noturno"; +"settings_fail_to_update_profile" = "Falha ao atualizar perfil"; +"settings_enable_push_notif" = "Notificações neste dispositivo"; +"settings_show_decrypted_content" = "Mostrar conteúdo descriptografado"; +"settings_global_settings_info" = "Configurações globais de notificação estão disponíveis no seu cliente web %@"; +"settings_on_denied_notification" = "As notificações são negadas para %@, permita-as nas configurações do seu dispositivo"; +"settings_enable_callkit" = "Chamada Integrada"; +"settings_callkit_info" = "Receba as chamadas na sua tela de bloqueio. Veja suas chamadas da Riot no histórico de chamadas do sistema. Se o iCloud estiver ativado, esse histórico de chamadas será compartilhado com a Apple."; +"settings_ui_language" = "Idioma"; +"settings_ui_theme" = "Tema"; +"settings_ui_theme_auto" = "Automático"; +"settings_ui_theme_light" = "Claro"; +"settings_ui_theme_dark" = "Escuro"; +"settings_ui_theme_black" = "Preto"; +"settings_ui_theme_picker_title" = "Selecione o tema"; +"settings_ui_theme_picker_message" = "\"Auto\" usa as configurações \"Inverter cores\" do seu dispositivo"; +"settings_unignore_user" = "Mostrar todas as mensagens de %@?"; +"settings_contacts_discover_matrix_users" = "Use e-mails e números de telefone para descobrir usuários"; +"settings_contacts_phonebook_country" = "País da lista telefônica"; +"settings_labs_e2e_encryption" = "Criptografia de ponta a ponta"; +"settings_labs_e2e_encryption_prompt_message" = "Para concluir a configuração da criptografia, você deve efetuar login novamente."; +"settings_labs_room_members_lazy_loading" = "Usuários de carrregamento Lazy de salas"; +"settings_labs_room_members_lazy_loading_error_message" = "Seu homeserver não suporta o carregamento Lazy de membros da sala ainda. Tente depois."; +"settings_labs_create_conference_with_jitsi" = "Crie chamadas de conferência com jitsi"; +"settings_version" = "Versão %@"; +"settings_olm_version" = "Versão do Olm %@"; +"settings_copyright" = "Copyright"; +"settings_copyright_url" = "https://riot.im/copyright"; +"settings_term_conditions" = "Termos e Condições"; +"settings_term_conditions_url" = "https://riot.im/tac_apple"; +"settings_privacy_policy" = "Política de Privacidade"; +"settings_privacy_policy_url" = "https://riot.im/privacy"; +"settings_third_party_notices" = "Avisos de terceiros"; +"settings_send_crash_report" = "Enviar dados de falha e uso anônimos"; +"settings_enable_rageshake" = "Agite rapidamente para relatar um erro"; +"settings_clear_cache" = "Limpar cache"; +"settings_change_password" = "Mudar senha"; +"settings_old_password" = "senha antiga"; +"settings_new_password" = "senha nova"; +"settings_confirm_password" = "confirme a senha"; +"settings_fail_to_update_password" = "Falha ao atualizar senha"; +"settings_password_updated" = "Sua senha foi atualizada"; +"settings_crypto_device_name" = "Nome do dispositivo "; +"settings_crypto_device_id" = "\nID do dispositivo: "; +"settings_crypto_device_key" = "\nChave do dispositivo: "; +"settings_crypto_export" = "Chaves de exportação"; +"settings_crypto_blacklist_unverified_devices" = "Criptografar apenas para dispositivos verificados"; +"settings_deactivate_my_account" = "Desativar minha conta"; +"room_details_people" = "Membros"; +"room_details_files" = "Arquivos"; +"room_details_settings" = "Configurações"; +"room_details_photo" = "Imagem da Sala"; +"room_details_room_name" = "Nome da sala"; +"room_details_topic" = "Tópico"; +"room_details_favourite_tag" = "Favorito"; +"room_details_low_priority_tag" = "Baixa prioridade"; +"room_details_mute_notifs" = "Silenciar notificações"; +"room_details_direct_chat" = "Chat direto"; +"room_details_access_section" = "Quem pode acessar esta sala?"; +"room_details_access_section_invited_only" = "Apenas pessoas que foram convidadas"; +"room_details_access_section_anyone_apart_from_guest" = "Quem conhece o link da sala, além dos convidados"; +"room_details_access_section_anyone" = "Qualquer pessoa que conheça o link da sala, incluindo convidados"; +"room_details_access_section_no_address_warning" = "Para ligar a uma sala, isto deve ter um endereço"; +"room_details_access_section_directory_toggle" = "Listar esta sala no diretório de salas"; +"room_details_history_section" = "Quem pode ler o histórico?"; +"room_details_history_section_anyone" = "Qualquer um"; +"room_details_history_section_members_only" = "Apenas membros (desde o momento em que selecionamos esta opção)"; +"room_details_history_section_members_only_since_invited" = "Apenas membros (desde que foram convidados)"; +"room_details_history_section_members_only_since_joined" = "Apenas membros (desde que aderiram)"; +"room_details_history_section_prompt_title" = "Aviso de privacidade"; +"room_details_history_section_prompt_msg" = "As alterações em quem pode ler o histórico só se aplicam a mensagens futuras nesta sala. A visibilidade do histórico existente não será alterada."; +"room_details_addresses_section" = "Endereços"; +"room_details_no_local_addresses" = "Este quarto não tem endereços locais"; +"room_details_new_address" = "adicionar novo endereço"; +"room_details_new_address_placeholder" = "Adicionar novo endereço (por exemplo, #foo%@)"; +"room_details_addresses_invalid_address_prompt_title" = "Formato de alias inválido"; +"room_details_addresses_invalid_address_prompt_msg" = "%@ não é um formato válido para um alias"; +"room_details_addresses_disable_main_address_prompt_title" = "Aviso de endereço principal"; +"room_details_addresses_disable_main_address_prompt_msg" = "Você não terá nenhum endereço principal especificado. O endereço principal padrão para esta sala será escolhido aleatoriamente"; +"room_details_flair_section" = "Mostrar insígnias para as comunidades"; +"room_details_new_flair_placeholder" = "Adicionar nova ID de comunidade (por exemplo, +foo%@)"; +"room_details_flair_invalid_id_prompt_title" = "Formato Inválido"; +"room_details_flair_invalid_id_prompt_msg" = "%@ não é um identificador válido para uma comunidade"; +"room_details_banned_users_section" = "Usuários banidos"; +"room_details_advanced_section" = "Avançado"; +"room_details_advanced_room_id" = "ID da sala:"; +"room_details_advanced_enable_e2e_encryption" = "Ativar criptografia (aviso: não pode ser desativado novamente!)"; +"room_details_advanced_e2e_encryption_enabled" = "A criptografia está ativada nesta sala"; +"room_details_advanced_e2e_encryption_disabled" = "A criptografia não está habilitada nesta sala."; +"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Criptografar apenas para dispositivos verificados"; +"room_details_fail_to_update_avatar" = "Falha ao atualizar a imagem da sala"; +"room_details_fail_to_update_room_name" = "Falha ao atualizar o nome da sala"; +"room_details_fail_to_update_topic" = "Falha ao atualizar o tópico"; +"room_details_fail_to_update_room_guest_access" = "Falha ao atualizar o acesso do convidado da sala"; +"room_details_fail_to_update_room_join_rule" = "Falha ao atualizar a regra de participação"; +"room_details_fail_to_update_room_directory_visibility" = "Falha ao atualizar a visibilidade da sala no diretório"; +"room_details_fail_to_update_history_visibility" = "Falha ao atualizar a visibilidade do histórico"; +"room_details_advanced_e2e_encryption_prompt_message" = "A criptografia de ponta a ponta é experimental e pode não ser confiável.\n\nVocê ainda não deve confiar nele para proteger os dados.\n\nOs dispositivos ainda não poderão descriptografar o histórico antes de ingressarem na sala.\n\nQuando a criptografia estiver ativada para uma sala, ela não poderá ser desativada novamente (por enquanto).\n\nAs mensagens criptografadas não estarão visíveis em clientes que ainda não implementam a criptografia."; +"room_details_fail_to_add_room_aliases" = "Falha ao adicionar os novos endereços da sala"; +"room_details_fail_to_remove_room_aliases" = "Falha ao remover os endereços da sala"; +"room_details_fail_to_update_room_canonical_alias" = "Falha ao atualizar o endereço principal"; +"room_details_fail_to_update_room_communities" = "Falha ao atualizar as comunidades relacionadas"; +"room_details_fail_to_update_room_direct" = "Falha ao atualizar o sinalizador direto desta sala"; +"room_details_fail_to_enable_encryption" = "Falha ao ativar a criptografia nesta sala"; +"room_details_save_changes_prompt" = "Você quer salvar as alterações?"; +"room_details_set_main_address" = "Definir como endereço principal"; +"room_details_unset_main_address" = "Desativar como endereço principal"; +"room_details_copy_room_id" = "Copiar o ID da sala"; +"room_details_copy_room_address" = "Copiar o endereço da sala"; +"room_details_copy_room_url" = "Copiar o URL da sala"; +// Group Details +"group_details_title" = "Detalhes da Comunidade"; +"group_details_home" = "Início"; +"group_details_people" = "Pessoas"; +"group_details_rooms" = "Salas"; +// Group Home +"group_home_one_member_format" = "1 membro"; +"group_home_multi_members_format" = "%tu membros"; +"group_home_one_room_format" = "1 sala"; +"group_home_multi_rooms_format" = "%tu salas"; +"group_invitation_format" = "%@ convidou você para participar desta comunidade"; +// Group participants +"group_participants_add_participant" = "Adicionar participante"; +"group_participants_leave_prompt_title" = "Sair do grupo"; +"group_participants_leave_prompt_msg" = "Tem certeza de que deseja sair do grupo?"; +"group_participants_remove_prompt_title" = "Confirmação"; +"group_participants_remove_prompt_msg" = "Tem certeza de que deseja remover %@ deste grupo?"; +"group_participants_invite_prompt_title" = "Confirmação"; +"group_participants_invite_prompt_msg" = "Tem certeza de que deseja convidar %@ para este grupo?"; +"group_participants_filter_members" = "Filtrar membros da comunidade"; +"group_participants_invite_another_user" = "Pesquisar / convidar por ID do usuário ou nome"; +"group_participants_invite_malformed_id_title" = "Erro ao convidar"; +"group_participants_invite_malformed_id" = "ID malformado. Deve ser um ID da Matrix como \"@localpart:domain\""; +"group_participants_invited_section" = "CONVIDADO"; +// Group rooms +"group_rooms_filter_rooms" = "Filtrar salas da comunidade"; +// Read Receipts +"read_receipts_list" = "Lista de confirmações de leitura"; +"receipt_status_read" = "Leitura: "; +// Media picker +"media_picker_library" = "Biblioteca"; +"media_picker_select" = "Selecionar"; +// Directory +"directory_title" = "Diretório"; +"directory_server_picker_title" = "Selecionar um diretório"; +"settings_display_name" = "Nome de Visualização"; +"settings_pin_rooms_with_missed_notif" = "Fixar sala com notificações perdidas"; +"settings_pin_rooms_with_unread" = "Fixar sala com mensagens não lidas"; +"directory_server_all_rooms" = "Todas as salas no servidor %@"; +"directory_server_all_native_rooms" = "Todas as salas nativas da Matrix"; +"directory_server_type_homeserver" = "Digite um homeserver para listar salas públicas dele"; +"directory_server_placeholder" = "matrix.org"; +// Events formatter +"event_formatter_member_updates" = "%tu mudanças na filiação"; +"event_formatter_widget_added" = "%@ widget adicionado por %@"; +"event_formatter_widget_removed" = "%@ widget removido por %@"; +"event_formatter_jitsi_widget_added" = "Conferência VoIP adicionada por %@"; +"event_formatter_jitsi_widget_removed" = "Conferência de VoIP removida por %@"; +"event_formatter_rerequest_keys_part1_link" = "Solicitar novamente chaves de criptografia"; +"event_formatter_rerequest_keys_part2" = " dos seus outros dispositivos."; +// Others +"or" = "ou"; +"you" = "Você"; +"today" = "Hoje"; +"yesterday" = "Ontem"; +"network_offline_prompt" = "A conexão com a Internet parece estar off-line."; +"homeserver_connection_lost" = "Não foi possível conectar-se ao homeserver."; +"public_room_section_title" = "Salas Públicas (em %@):"; +"bug_report_prompt" = "O aplicativo encerrou abruptamente na última vez. Gostaria de enviar um relatório de falhas?"; +"rage_shake_prompt" = "Você parece estar agitando o telefone em frustração. Gostaria de enviar um relatório de erro?"; +"do_not_ask_again" = "Não pergunte novamente"; +"camera_access_not_granted" = "%@ não tem permissão para usar a câmera, altere as configurações de privacidade"; +"large_badge_value_k_format" = "%.1fK"; +// Call +"call_incoming_voice_prompt" = "Recebendo chamada de voz de %@"; +"call_incoming_video_prompt" = "Recebendo chamada de vídeo de %@"; +"call_incoming_voice" = "Recebendo chamada..."; +"call_incoming_video" = "Recebendo chamada de vídeo..."; +"call_already_displayed" = "Já existe uma chamada em andamento."; +"call_jitsi_error" = "Não foi possível participar da conferência."; +// No VoIP support +"no_voip_title" = "Recebendo chamada"; +"no_voip" = "%@ está chamando você, mas %@ ainda não suporta chamadas.\nVocê pode ignorar essa notificação e atender a chamada de outro dispositivo ou pode rejeitá-la."; +// Crash report +"google_analytics_use_prompt" = "Você gostaria de ajudar a melhorar o %@ informando automaticamente relatórios de falhas e dados de uso anônimos?"; +// Crypto +"e2e_enabling_on_app_update" = "O Riot agora suporta criptografia de ponta a ponta, mas você precisa fazer login novamente para ativá-lo.\n\nVocê pode fazer isso agora ou mais tarde a partir das configurações do aplicativo."; +"e2e_need_log_in_again" = "Você precisa fazer login novamente para gerar chaves de criptografia de ponta a ponta para este dispositivo e enviar a chave pública para o seu homeserver.\nIsso é apenas dessa vez; Desculpe pela inconveniência."; +// Bug report +"bug_report_title" = "Relatório de erro"; +"bug_report_description" = "Por favor, descreva o erro. O que você fez? O que você esperava que acontecesse? O que realmente aconteceu?"; +"bug_crash_report_title" = "Relatório de erros"; +"bug_crash_report_description" = "Por favor, descreva o que você fez antes do erro:"; +"bug_report_logs_description" = "Para diagnosticar problemas, os logs deste cliente serão enviados com este relatório de erro. Se preferir enviar apenas o texto acima, por favor desmarque:"; +"bug_report_send_logs" = "Enviar logs"; +"bug_report_send_screenshot" = "Enviar screenshot"; +"bug_report_progress_zipping" = "Coletando logs"; +"bug_report_progress_uploading" = "Enviando relatório"; +"bug_report_send" = "Enviar"; +// Widget +"widget_no_power_to_manage" = "Você precisa de permissão para gerenciar widgets nesta sala"; +"widget_creation_failure" = "A criação do widget falhou"; +"widget_sticker_picker_no_stickerpacks_alert" = "Atualmente, você não tem nenhum stickerpacks ativado."; +"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Adicione alguns agora?"; +// Widget Integration Manager +"widget_integration_need_to_be_able_to_invite" = "Você precisa convidar os usuários para fazer isso."; +"widget_integration_unable_to_create" = "Não é possível criar widget."; +"widget_integration_failed_to_send_request" = "Falha ao enviar solicitação."; +"widget_integration_room_not_recognised" = "Esta sala não é reconhecida."; +"widget_integration_positive_power_level" = "O nível de permissão deve ser inteiro positivo."; +"widget_integration_must_be_in_room" = "Você não está nesta sala."; +"widget_integration_no_permission_in_room" = "Você não tem permissão para fazer isso nesta sala."; +"widget_integration_missing_room_id" = "room_id ausente em pedido."; +"widget_integration_missing_user_id" = "user_id ausente em pedido."; +"widget_integration_room_not_visible" = "Sala %@ não é visível."; +// Share extension +"share_extension_auth_prompt" = "Entre no aplicativo principal para compartilhar conteúdo"; +"share_extension_failed_to_encrypt" = "Falha ao enviar. Verifique no aplicativo principal as configurações de criptografia para esta sala"; +// Room key request dialog +"e2e_room_key_request_title" = "Pedido de chave de encriptação"; +"e2e_room_key_request_message_new_device" = "Você adicionou um novo dispositivo '%@', que está solicitando chaves de criptografia."; +"e2e_room_key_request_message" = "Seu dispositivo não verificado '%@' está solicitando chaves de criptografia."; +"e2e_room_key_request_start_verification" = "Iniciar verificação..."; +"e2e_room_key_request_share_without_verifying" = "Compartilhar sem verificar"; +"e2e_room_key_request_ignore_request" = "Ignorar requisição"; +// GDPR +"gdpr_consent_not_given_alert_message" = "Para continuar usando o %@ homeserver, você deve revisar e concordar com os termos e condições."; +"gdpr_consent_not_given_alert_review_now_action" = "Revisar agora"; +"deactivate_account_title" = "Desativar Conta"; +"deactivate_account_informations_part1" = "Isso tornará sua conta permanentemente inutilizável. Você não poderá efetuar login e ninguém poderá registrar novamente o mesmo ID de usuário. Isso fará com que sua conta deixe todas as salas nas quais está participando e removerá os detalhes da sua conta do seu servidor de identidade. "; +"deactivate_account_informations_part2_emphasize" = "Essa ação é irreversível."; +"deactivate_account_informations_part3" = "\n\nDesativando sua conta "; +"deactivate_account_informations_part4_emphasize" = "não nos faz esquecer, por padrão, as mensagens que você enviou. "; +"deactivate_account_informations_part5" = "Se você gostaria que nós esquecêssemos suas mensagens, por favor marque a caixa abaixo\n\nA visibilidade das mensagens no Matrix é semelhante ao e-mail. O fato de esquecermos suas mensagens significa que as mensagens que você enviou não serão compartilhadas com usuários novos ou não registrados, mas os usuários registrados que já têm acesso a essas mensagens ainda terão acesso à sua cópia."; +"deactivate_account_forget_messages_information_part1" = "Por favor, esqueça todas as mensagens que enviei quando minha conta está desativada ("; +"deactivate_account_forget_messages_information_part2_emphasize" = "Aviso"; +"deactivate_account_forget_messages_information_part3" = ": isso fará com que futuros usuários vejam uma visão incompleta das conversas)"; +"deactivate_account_validate_action" = "Desativar conta"; +"deactivate_account_password_alert_title" = "Desativar Conta"; +"deactivate_account_password_alert_message" = "Para continuar, por favor, digite sua senha"; +// Re-request confirmation dialog +"rerequest_keys_alert_title" = "Requisição enviada"; +"rerequest_keys_alert_message" = "Por favor, inicie o Riot em outro dispositivo que possa descriptografar a mensagem para que ela possa enviar as chaves para este dispositivo."; diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index c3ef0320e..f295b9993 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -404,7 +404,7 @@ "unknown_devices_alert" = "Эта комната содержит устройства, которые еще не были проверены.\nЭто означает, что нет гарантии, что устройства принадлежат пользователям, тем, за кого они сами себя выдают.\nПеред продолжением рекомендуется пройти процедуру проверки для каждого устройства, но при желании можно повторно отправить сообщение без проверки."; "room_preview_try_join_an_unknown_room" = "Вы пытаетесь получить доступ к %@. Хотите присоединиться к обсуждению?"; "settings_config_no_build_info" = "Нет информации о выпуске"; -"settings_sign_out_e2e_warn" = "Будут утеряны сквозные ключи шифрования. Это означает, что вы больше не сможете читать старые сообщения в зашифрованных комнатах на этом устройстве."; +"settings_sign_out_e2e_warn" = "Будут утеряны ключи сквозного шифрования. Это означает, что вы больше не сможете читать старые сообщения в зашифрованных комнатах на этом устройстве."; "settings_remove_email_prompt_msg" = "Вы действительно хотите удалить адрес электронной почты %@?"; "settings_remove_phone_prompt_msg" = "Вы действительно хотите удалить номер телефона %@?"; "settings_pin_rooms_with_missed_notif" = "Закрепить комнаты с пропущенными уведомлениями"; @@ -544,16 +544,129 @@ "room_replacement_information" = "Эта комната была заменена и больше не активна."; "room_replacement_link" = "Этот разговор продолжается здесь."; "room_predecessor_information" = "Эта комната является продолжением другого разговора."; -"room_predecessor_link" = "Нажмите здесь, чтобы просмотреть старые сообщения."; +"room_predecessor_link" = "Коснитесь здесь, чтобы просмотреть старые сообщения."; "room_recents_server_notice_section" = "СИСТЕМНЫЕ ПРЕДУПРЕЖДЕНИЯ"; "room_resource_limit_exceeded_message_contact_1" = " Пожалуйста "; "room_resource_limit_exceeded_message_contact_2_link" = "обратитесь к администратору сервиса"; "room_resource_limit_exceeded_message_contact_3" = " для продолжения его использования."; "room_event_action_view_decrypted_source" = "Посмотреть расшифрованные исходники"; -"room_resource_usage_limit_reached_message_1_default" = "Этот сервер превысил один из лимитов ресурсов, так что "; -"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Этот сервер превысил месячный лимит активных пользователей, так что "; +"room_resource_usage_limit_reached_message_1_default" = "Превышен один из ресурсных лимитов сервера, по этому "; +"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Сервер достиг ежемесячного ограничения активных пользователей, так что "; "room_resource_usage_limit_reached_message_2" = "некоторые пользователи не смогут залогиниться."; "room_resource_usage_limit_reached_message_contact_3" = " что бы увеличить лимит."; "settings_labs_room_members_lazy_loading" = "Ленивая подгрузка собеседников"; "settings_labs_room_members_lazy_loading_error_message" = "Ваш сервер не поддерживает ленивую подгрузку собеседников. Попробуйте позже."; "homeserver_connection_lost" = "Невозможно соединиться с этим сервером."; +"auth_accept_policies" = "Пожалуйста ознакомьтесь и подтвердите согласие с политикой этого сервера:"; +"settings_key_backup_info_checking" = "Проверка ..."; +"settings_key_backup_info_none" = "Ваши ключи не сохраняются с этого устройства."; +"settings_key_backup" = "РЕЗЕРВНАЯ КОПИЯ КЛЮЧЕЙ"; +"settings_key_backup_info_version" = "Версия ключа резервного копирования: %@"; +"settings_key_backup_info_algorithm" = "Алгоритм: %@"; +"settings_key_backup_info_valid" = "Это устройство делает резервное копирование ключей."; +"settings_key_backup_info_not_valid" = "Это устройство не делает резервного копирования ключей."; +"settings_key_backup_info_progress" = "Резервное копирование ключей %@..."; +"settings_key_backup_info_progress_done" = "Все ключи сохранены"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "Для Безопасного Восстановления Сообщений на этом устройстве, проверьте %@."; +"settings_key_backup_info_not_trusted_fix_action" = "Для Безопасного Восстановления Сообщений на этом устройстве, требуется пароль или ключ восстановления."; +"settings_key_backup_info_trust_signature_unknown" = "Резервная копия подписана устройством с идентификатором: %@"; +"settings_key_backup_info_trust_signature_valid" = "Резервная копия имеет валидную подпись этого устройства"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Резервная копия имеет валидную подпись %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Резервная копия подписана %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Резервная копия имеет невалидную подпись %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Резервная копия имеет невалидную подпись %@"; +"settings_key_backup_button_create" = "Использовать резервное копирование ключей"; +"settings_key_backup_button_restore" = "Восстановить из резервной копии"; +"settings_key_backup_button_delete" = "Удалить резервную копию"; +"settings_key_backup_button_verify" = "Проверить"; +"settings_key_backup_delete_confirmation_prompt_title" = "Удалить резервную копию"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Вы уверены? Зашифрованные сообщения будут утеряны, если не сделать резервную копию должным образом."; +"room_does_not_exist" = "%@ не существует"; +"key_backup_setup_title" = "Резервное копирование ключей"; +"key_backup_setup_skip_action" = "Пропустить"; +"key_backup_setup_skip_alert_title" = "Вы уверены?"; +"key_backup_setup_skip_alert_message" = "Вы можете потерять зашифрованные сообщения, если выйдете из системы или потеряете свое устройство."; +"key_backup_setup_skip_alert_skip_action" = "Пропустить"; +"key_backup_setup_intro_title" = "Никогда не теряйте зашифрованных сообщений"; +"key_backup_setup_intro_info" = "Сообщения в зашифрованных комнатах защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений.\n\nСохраните ключи надежно, чтобы не потерять их."; +"key_backup_setup_intro_setup_action" = "Настроить"; +"key_backup_setup_passphrase_info" = "Мы будем хранить зашифрованную копию ваших ключей на нашем сервере. Для безопасности, защитите резервную копию парольной фразой.\n\nДля обеспечения максимальной безопасности он должен отличаться от пароля учетной записи."; +"key_backup_setup_passphrase_passphrase_title" = "Ввод"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Введите парольную фразу"; +"key_backup_setup_passphrase_passphrase_valid" = "Отлично!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Попробуйте добавить слово"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Подтвердить"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Повторите парольную фразу"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Отлично!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Парольные фразы не совпадает"; +"key_backup_setup_passphrase_set_passphrase_action" = "Установить парольную фразу"; +"key_backup_setup_recovery_key_recovery_key_title" = "Ключ для восстановления"; +"key_backup_setup_recovery_key_make_copy_action" = "Сделать копию"; +"key_backup_setup_recovery_key_made_copy_action" = "Я сделал копию"; +"key_backup_recover_title" = "Безопасные Сообщения"; +"key_backup_recover_empty_backup_title" = "Пустая резервная копия"; +"key_backup_recover_empty_backup_message" = "Нет ключа для восстановления"; +"key_backup_recover_from_passphrase_info" = "Используйте парольную фразу восстановления для разблокировки истории безопасных сообщений"; +"key_backup_recover_from_passphrase_passphrase_title" = "Ввод"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Введите парольную фразу"; +"key_backup_recover_from_passphrase_recover_action" = "Разблокировать историю"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не знаете вашу парольную фразу для восстановления? Вы можете "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "используйте ключ восстановления"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Используйте ключ восстановления для разблокировки истории безопасных сообщений"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Ввод"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Введите ключ восстановления"; +"key_backup_recover_from_recovery_key_recover_action" = "Разблокировать историю"; +"key_backup_recover_success_info" = "Резервная копия восстановлена!"; +"key_backup_recover_done_action" = "Готово"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Потеряли ключ восстановления? В настройках вы можете создать новый."; +"key_backup_setup_banner_title_part1" = "Настройка Безопасного Восстановления Сообщений"; +"key_backup_recover_banner_title_part1" = "Запуск Безопасного Восстановления Сообщений"; +"key_backup_recover_banner_title_part2" = " для чтения истории зашифрованных сообщений на этом устройстве"; +"key_backup_setup_banner_title_part2" = " чтобы никогда не потерять зашифрованные сообщения"; +"key_backup_setup_recovery_key_info" = "Сделайте копию ключа восстановления и надёжно сохраните его.\n\nЕго можно будет использовать для восстановления истории зашифрованных сообщений, на случай если вы забудете парольную фразу восстановления."; +"key_backup_setup_success_title" = "Успешно!"; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Сохранить ключ восстановления"; +"key_backup_setup_success_from_passphrase_done_action" = "Готово"; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Ключ восстановления"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Сделать копию"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Я сделал копию"; +"key_backup_recover_invalid_passphrase_title" = "Неверная парольная фраза для восстановления"; +"key_backup_recover_invalid_recovery_key_title" = "Несоответствующий ключ восстановления"; +"key_backup_setup_banner_title" = "Не теряйте зашифрованные сообщения"; +"key_backup_setup_banner_subtitle" = "Начать использовать ключ восстановления"; +"key_backup_recover_banner_title" = "Не теряйте зашифрованные сообщения"; +"key_backup_recover_banner_subtitle" = "Использовать ключ восстановления"; +"sign_out_existing_key_backup_alert_title" = "Вы уверены, что хотите выйти?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Выйти"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Начать использовать ключ восстановления"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Резервная копия"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Выйти"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Я подожду"; +"settings_key_backup_button_use" = "Использовать ключ восстановления"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Начать использовать ключ восстановления"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Использовать ключ восстановления"; +"settings_key_backup_info" = "Зашифрованные сообщения защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений."; +"settings_key_backup_info_signout_warning" = "Перед выходом сделайте резервную копию ключей, чтобы не потерять их."; +"key_backup_setup_passphrase_title" = "Защитите резервную копию парольной фразой"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Или защитите резервную копию с помощью ключа восстановления, сохранив его в безопасном месте."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Расширенный) Настройка с ключом восстановления"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Выполняется резервная копия ключей.\n\nКлюч восстановления это страховка — вы можете использовать его для восстановления доступа к вашим зашифрованным сообщениям, если забыли пароль.\n\nХраните ключ восстановления в очень надежном месте, например, в менеджере паролей (или сейфе)."; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Выполняется резервная копия ключей.\n\nСохраните его в безопасном месте."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Мне не нужны мои зашифрованные сообщения"; +"sign_out_key_backup_in_progress_alert_title" = "Идет процесс резервного копирования. Если вы выйдете сейчас, то потеряете доступ к зашифрованным сообщениям."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Вы потеряете доступ к зашифрованным сообщениям, если не сделаете резервное копирование ключей перед выходом."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Зашифрованные сообщения будут утеряны"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Мне не нужны мои зашифрованные сообщения"; +"sign_out_non_existing_key_backup_alert_title" = "Вы потеряете доступ к зашифрованным сообщениям если выйдете сейчас"; +"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой парольной фразы: убедитесь, что вы ввели верную парольную фразу для восстановления."; +"key_backup_recover_invalid_recovery_key" = "Невозможно расшифровать резервную копию с помощью этого ключа: убедитесь, что вы ввели верный ключ восстановления."; +"e2e_key_backup_wrong_version_button_settings" = "Настойки"; +"key_backup_setup_intro_manual_export_info" = "(Расширенный)"; +"e2e_key_backup_wrong_version_button_wasme" = "Это был я"; +"key_backup_setup_intro_manual_export_action" = "Ручной экспорт ключей"; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Новая резервная копия ключей"; +"e2e_key_backup_wrong_version" = "Обнаружена новая резервная копия ключа безопасного сообщения.\n\nЕсли вы этого не делали, установите новую парольную фразу в настройках."; diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index acbcfbe0d..31538a2ad 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -547,9 +547,114 @@ "deactivate_account_forget_messages_information_part1" = "Të lutem, harro krejt mesazhet që kamë dërguar, kur të çaktivizohet llogaria ime ("; "deactivate_account_forget_messages_information_part3" = ": kjo do të bëjë që përdorues të ardhshëm të shohin një pamje jo të plotë të bisedave)"; "rerequest_keys_alert_message" = "Ju lutemi, niseni Riot-in në një tjetër pajisje që mundet të shfshehtëzojë mesazhin, që kështu të mund të dërgojë kyçet te kjo pajisje."; -"room_event_action_redact" = "Redaktojeni"; +"room_event_action_redact" = "Hiqe"; "e2e_need_log_in_again" = "Që të prodhohen kyçe fshehtëzimi skaj-më-skaj për këtë pajisje, lypset të ribëni hyrjen dhe të parashtroni kyçin publik te shërbyesi juaj homë.\nKjo duhet vetëm një herë; na ndjeni për belanë."; "room_action_send_sticker" = "Dërgoni ngjitës"; "settings_flair" = "Shfaq simbole, kur lejohet"; "room_details_flair_section" = "Shfaq simbole për bashkësi"; "auth_accept_policies" = "Ju lutemi, shqyrtoni dhe pranoni rregullat e këtij shërbyesi Home:"; +"settings_key_backup" = "KOPJERUAJTE KYÇI"; +"settings_key_backup_info_checking" = "Po kontrollohet…"; +"settings_key_backup_info_none" = "Kyçet tuaj nuk po kopjeruhen nga kjo pajisje."; +"settings_key_backup_info_version" = "Version Kopjeruajtjeje Kyçesh: %@"; +"settings_key_backup_info_algorithm" = "Algoritëm: %@"; +"settings_key_backup_info_valid" = "Kjo pajisje po bën kopjeruajtje të kyçeve tuaja."; +"settings_key_backup_info_not_valid" = "Kjo pajisje nuk po bën kopjeruajtje të kyçeve tuaja."; +"settings_key_backup_info_progress" = "Po kopjeruhen kyçet për %@…"; +"settings_key_backup_info_progress_done" = "U kopjeruajtën krejt kyçet"; +"settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "Për të përdorur Rikthim Mesazhesh të Sigurt në këtë pajisje, verifikoni tani %@."; +"settings_key_backup_info_not_trusted_fix_action" = "Për të përdorur Rikthim Mesazhesh të Sigurt në këtë pajisje, jepni tani një frazëkalim ose një kyç rikthimesh."; +"settings_key_backup_info_trust_signature_unknown" = "Kopjeruajtja ka një nënshkrim nga një pajisje me ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "Kopjeruajtja ka një nënshkrim të vlefshëm prej kësaj pajisjeje"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Kopjeruajtja ka një nënshkrim të vlefshëm prej %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Kopjeruajtja ka nënshkrim nga %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Kopjeruajtja ka një nënshkrim të pavlefshëm prej %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Kopjeruajtja ka një nënshkrim të pavlefshëm prej %@"; +"settings_key_backup_button_create" = "Fillo të përdorësh Kopjeruajtje Kyçesh"; +"settings_key_backup_button_restore" = "Riktheje prej Kopjeruajtje"; +"settings_key_backup_button_delete" = "Fshije Kopjeruajtjen"; +"settings_key_backup_button_verify" = "Verifikoje"; +"settings_key_backup_delete_confirmation_prompt_title" = "Fshije Kopjeruajtjen"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Jeni i sigurt? Do të humbni mesazhet tuaj të fshehtëzuar, nëse kopjeruajtja për kyçet tuaj nuk bëhet si duhet."; +"room_does_not_exist" = "%@ nuk ekziston"; +"key_backup_setup_title" = "Kopjeruajtje Kyçi"; +"key_backup_setup_skip_action" = "Anashkaloje"; +"key_backup_setup_skip_alert_title" = "A jeni i sigurt?"; +"key_backup_setup_skip_alert_message" = "Nëse dilni nga llogaria, ose nëse humbni pajisjen tuaj, mund të humbni mesazhe të sigurt."; +"key_backup_setup_skip_alert_skip_action" = "Anashkaloje"; +"key_backup_setup_intro_title" = "Mos humbni kurrë mesazhe të fshehtëzuar"; +"key_backup_setup_intro_info" = "Mesazhet në dhoma të fshehtëzuara sigurohen me fshehtëzim skaj-më-skaj. Vetëm ju dhe marrësi(t) keni kyçet për leximin e këtyre mesazheve.\n\nBëni një kopjeruajtje të sigurt të kyçeve tuaj, për të shmangur humbjen e tyre."; +"key_backup_setup_intro_setup_action" = "Rregulloje"; +"key_backup_setup_passphrase_info" = "Do të depozitojmë një kopje të fshehtëzuar të kyçeve tuaj në shërbyesin tonë. Mbrojeni kopjeruajtjen tuaj me një frazëkalim, për ta mbajtur të parrezikuar.\n\nPër maksimumin e sigurisë, ky duhet të jetë i ndryshëm nga fjalëkalimi juaj për llogarinë."; +"key_backup_setup_passphrase_passphrase_title" = "Jepeni"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Jepni frazëkalimin"; +"key_backup_setup_passphrase_passphrase_valid" = "Bukur!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Provoni të shtoni një fjalë"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Ripohojeni"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Ripohoni frazëkalimin"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Bukur!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Frazëkalimet nuk përputhen"; +"key_backup_setup_passphrase_set_passphrase_action" = "Caktoni Frazëkalim"; +"key_backup_setup_recovery_key_info" = "Bëni një kopje të këtij Kyçi Rikthimesh dhe ruajeni diku.\n\nSi rrjet sigurie, mund ta përdorni për të rikthyer historikun e mesazheve tuaj të fshehtëzuar, nëse harroni Frazëkalimin e Rikthimeve."; +"key_backup_setup_recovery_key_recovery_key_title" = "Kyç Rikthimesh"; +"key_backup_setup_recovery_key_make_copy_action" = "Bëni një Kopje"; +"key_backup_setup_recovery_key_made_copy_action" = "Kam bërë një kopje"; +"key_backup_recover_title" = "Mesazhe të Sigurt"; +"key_backup_recover_empty_backup_title" = "Kopjeruajtje e zbrazët"; +"key_backup_recover_empty_backup_message" = "S’ka kyç për rikthim"; +"key_backup_recover_from_passphrase_info" = "Që të shkyçni historikun e mesazheve tuaj të sigurt përdorni frazëkalimin tuaj të rikthimeve"; +"key_backup_recover_from_passphrase_passphrase_title" = "Jepeni"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Jepni Frazëkalimin"; +"key_backup_recover_from_passphrase_recover_action" = "Shkyçeni Historikun"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Nuk e dini frazëkalimin tuaj të rikthimeve? Mundeni të "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "përdorni kyçin tuaj të rikthimeve"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Përdorni kyçin tuaj të rikthimeve për të shkyçur historikun tuaj të mesazheve të sigurt"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Jepeni"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Jepni Kyç Rikthimi"; +"key_backup_recover_from_recovery_key_recover_action" = "Shkyçe Historikun"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Humbët kyçin tuaj të rikthimeve? Te rregullimet mund të caktoni një të ri."; +"key_backup_recover_success_info" = "Kopjeruajtja u Rikthye!"; +"key_backup_recover_done_action" = "U bë"; +"key_backup_setup_banner_title_part1" = "Rregulloni Rikthim Mesazhesh të Sigurt"; +"key_backup_setup_banner_title_part2" = " që të mos humbni kurrë mesazhe të fshehtëzuar"; +"key_backup_recover_banner_title_part1" = "Xhironi Rikthim Mesazhesh të Sigurt"; +"key_backup_recover_banner_title_part2" = " që të lexoni historik mesazhesh të fshehtëzuar në këtë pajisje"; +"settings_key_backup_info" = "Mesazhet e fshehtëzuar sigurohen me fshehtëzim skaj-më-skaj. Vetëm ju dhe marrësi(t) kanë kyçet për të lexuar këto mesazhe."; +"settings_key_backup_info_signout_warning" = "Kopjeruajini kyçet tuaj, përpara se të dilni, që të shmangni humbjen e tyre."; +"settings_key_backup_button_use" = "Përdor kopjeruajtje kyçesh"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Fillo të përdorësh Kopjeruajtje Kyçesh"; +"key_backup_setup_intro_setup_action_with_existing_backup" = "Përdor Kopjeruajtje Kyçesh"; +"key_backup_setup_passphrase_title" = "Sigurojeni kopjeruajtjen tuaj me një Frazëkalim"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Ose, sigurojeni kopjeruajtjen tuaj me një Kyç Rikthimesh, duke e ruajtur këtë diku të parrezikuar."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Të mëtejshme) Rregullojeni me një Kyç Rikthimesh"; +"key_backup_setup_success_title" = "Sukses!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Po bëhet kopjeruajtja për kyçet tuaj.\n\nKyçi juaj i rikthimeve është një lloj rrjeti sigurie - mund ta përdorni për të rifituar hyrje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.\n\nMbajeni kyçin tuaj të rikthimeve diku shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Ruani Kyç Rikthimesh"; +"key_backup_setup_success_from_passphrase_done_action" = "U krye"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Po bëhet kopjeruajtja për kyçet tuaj.\n\nBëni një kopje të këtij kyçi rikthimesh dhe mbajeni të parrezikuar."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Kyç Rikthimesh"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Bëni një Kopje"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Kam bërë një kopje"; +"key_backup_recover_invalid_passphrase_title" = "Frazëkalim Rikthimi i Pasaktë"; +"key_backup_recover_invalid_passphrase" = "S’u shfshehtëzua dot kopjeruajtja me këtë frazëkalim: ju lutemi, verifikoni që dhatë frazëkalimin e duhur të rikthimeve."; +"key_backup_recover_invalid_recovery_key_title" = "Mospërputhje Kyçesh Rikthimi"; +"key_backup_recover_invalid_recovery_key" = "Nuk u shfshehtëzua dot kopjeruajtja me këtë kyç: ju lutemi, verifikoni që dhatë kyçin e duhur të rikthimeve."; +"key_backup_setup_banner_title" = "Mos humbni kurrë mesazhe të fshehtëzuar"; +"key_backup_setup_banner_subtitle" = "Fillo të përdorësh Kopjeruajtje Kyçesh"; +"key_backup_recover_banner_title" = "Mos humbni kurrë mesazhe të fshehtëzuar"; +"key_backup_recover_banner_subtitle" = "Përdor Kopjeruajtje Kyçesh"; +"sign_out_existing_key_backup_alert_title" = "Jeni i sigurt se doni të dilni?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Dilni"; +"sign_out_non_existing_key_backup_alert_title" = "Nëse dilni tani nga llogaria, do të humbni mesazhet tuaj të sigurt"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Fillo të përdorësh Kopjeruajtje Kyçesh"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Nuk i dua mesazhet e mia të fshehtëzuar"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Do të humbni mesazhet tuaj të fshehtëzuar"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Do të humbni hyrjen te mesazhet tuaj të fshehtëzuar, veç në bëfshi kopjeruajtje të kyçeve tuaj, përpara se të dilni nga llogaria."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Dilni"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Kopjeruajtje"; +"sign_out_key_backup_in_progress_alert_title" = "Kopjeruajtja e kyçeve po kryhet. Nëse dilni tani nga llogaria, do të humbni hyrjen te mesazhet tuaj të fshehtëzuar."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Nuk i dua mesazhet e mia të fshehtëzuar"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Do të pres"; diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index dabdb7af2..92e6044b6 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -19,7 +19,8 @@ #import "RoomBubbleCellData.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import @@ -61,7 +62,7 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date]; timeLabel.textAlignment = NSTextAlignmentRight; - timeLabel.textColor = kRiotSecondaryTextColor; + timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight]; @@ -208,7 +209,7 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT markPosY, VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_WIDTH, markHeight)]; - markerView.backgroundColor = kRiotColorGreen; + markerView.backgroundColor = ThemeService.shared.theme.tintColor; [markerView setTranslatesAutoresizingMaskIntoConstraints:NO]; markerView.accessibilityIdentifier = @"markerView"; @@ -263,7 +264,7 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT timeLabel.text = [bubbleData.eventFormatter dateStringFromDate:date withTime:NO]; timeLabel.textAlignment = NSTextAlignmentRight; - timeLabel.textColor = kRiotSecondaryTextColor; + timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight]; @@ -315,12 +316,12 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT - (void)setBlurred:(BOOL)blurred { - objc_setAssociatedObject(self, @selector(blurred), [NSNumber numberWithBool:blurred], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(self, @selector(blurred), @(blurred), OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (blurred) { self.bubbleOverlayContainer.hidden = NO; - self.bubbleOverlayContainer.backgroundColor = kRiotPrimaryBgColor; + self.bubbleOverlayContainer.backgroundColor = ThemeService.shared.theme.backgroundColor; self.bubbleOverlayContainer.alpha = 0.8; self.bubbleOverlayContainer.userInteractionEnabled = YES; diff --git a/Riot/Categories/MXRoom+Riot.m b/Riot/Categories/MXRoom+Riot.m index fc2533508..cc43e075a 100644 --- a/Riot/Categories/MXRoom+Riot.m +++ b/Riot/Categories/MXRoom+Riot.m @@ -32,7 +32,7 @@ if (self.accountData.tags && self.accountData.tags.count) { - oldTag = [self.accountData.tags.allKeys objectAtIndex:0]; + oldTag = self.accountData.tags.allKeys[0]; } // support only kMXRoomTagFavourite or kMXRoomTagLowPriority tags by now diff --git a/Riot/Categories/OperationQueue.swift b/Riot/Categories/OperationQueue.swift new file mode 100644 index 000000000..0b3e73ebd --- /dev/null +++ b/Riot/Categories/OperationQueue.swift @@ -0,0 +1,35 @@ +/* + 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 + +extension OperationQueue { + + class func vc_createSerialOperationQueue(name: String? = nil) -> OperationQueue { + let coordinatorDelegateQueue = OperationQueue() + coordinatorDelegateQueue.name = name + coordinatorDelegateQueue.maxConcurrentOperationCount = 1 + return coordinatorDelegateQueue + } + + func vc_pause() { + self.isSuspended = true + } + + func vc_resume() { + self.isSuspended = false + } +} diff --git a/Riot/Categories/UIButton.swift b/Riot/Categories/UIButton.swift new file mode 100644 index 000000000..01c1095f4 --- /dev/null +++ b/Riot/Categories/UIButton.swift @@ -0,0 +1,32 @@ +/* + 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 + +extension UIButton { + + /// Enable multiple lines for button title. + /// + /// - Parameter textAlignment: Title text alignement. Default `NSTextAlignment.center`. + func vc_enableMultiLinesTitle(textAlignment: NSTextAlignment = .center) { + guard let titleLabel = self.titleLabel else { + return + } + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.numberOfLines = 0 + titleLabel.textAlignment = textAlignment + } +} diff --git a/Riot/Categories/UIColor.swift b/Riot/Categories/UIColor.swift new file mode 100644 index 000000000..6095c4fbf --- /dev/null +++ b/Riot/Categories/UIColor.swift @@ -0,0 +1,28 @@ +/* + 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 + +@objc extension UIColor { + convenience init(rgb: UInt) { + self.init( + red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgb & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } +} diff --git a/Riot/Categories/UIControl.swift b/Riot/Categories/UIControl.swift new file mode 100644 index 000000000..a6323581a --- /dev/null +++ b/Riot/Categories/UIControl.swift @@ -0,0 +1,38 @@ +/* + 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 + +// Source: https://stackoverflow.com/a/44917661 +class ClosureSleeve { + let closure: () -> () + + init(attachTo: AnyObject, closure: @escaping () -> ()) { + self.closure = closure + objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN) + } + + @objc func invoke() { + closure() + } +} + +extension UIControl { + func vc_addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: @escaping () -> ()) { + let sleeve = ClosureSleeve(attachTo: self, closure: action) + addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents) + } +} diff --git a/Riot/Categories/UIView.swift b/Riot/Categories/UIView.swift new file mode 100644 index 000000000..5a4652104 --- /dev/null +++ b/Riot/Categories/UIView.swift @@ -0,0 +1,34 @@ +/* + 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 + +extension UIView { + + /// Add a subview matching parent view using autolayout + func vc_addSubViewMatchingParent(_ subView: UIView) { + self.addSubview(subView) + subView.translatesAutoresizingMaskIntoConstraints = false + let views = ["view": subView] + ["H:|[view]|", "V:|[view]|"].forEach { vfl in + let constraints = NSLayoutConstraint.constraints(withVisualFormat: vfl, + options: [], + metrics: nil, + views: views) + constraints.forEach { $0.isActive = true } + } + } +} diff --git a/Riot/Categories/UIViewController+RiotSearch.m b/Riot/Categories/UIViewController+RiotSearch.m index becfd114c..7f4fe2eef 100644 --- a/Riot/Categories/UIViewController+RiotSearch.m +++ b/Riot/Categories/UIViewController+RiotSearch.m @@ -19,7 +19,8 @@ #import -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" /** `UIViewControllerRiotSearchInternals` is the internal single point storage for the search feature. @@ -94,8 +95,7 @@ self.searchBar.text = @""; // Customize search bar - self.searchBar.barStyle = kRiotDesignSearchBarStyle; - self.searchBar.tintColor = kRiotDesignSearchBarTintColor; + [ThemeService.shared.theme applyStyleOnSearchBar:self.searchBar]; // Remove navigation buttons self.navigationItem.hidesBackButton = YES; @@ -134,7 +134,7 @@ - (void)addBackgroundImageViewToView:(UIView*)view { - UIImage *searchBgImage = [MXKTools paintImage:[UIImage imageNamed:@"search_bg"] withColor:kRiotKeyboardColor]; + UIImage *searchBgImage = [MXKTools paintImage:[UIImage imageNamed:@"search_bg"] withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:searchBgImage]; backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO; diff --git a/Riot/Categories/UIViewController.swift b/Riot/Categories/UIViewController.swift new file mode 100644 index 000000000..2b0e16155 --- /dev/null +++ b/Riot/Categories/UIViewController.swift @@ -0,0 +1,64 @@ +/* + 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 + +extension UIViewController { + + /// Remove back bar button title when pushing a view controller. + /// This method should be called on the previous controller in UINavigationController stack. + func vc_removeBackTitle() { + self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) + } + + + /// Add a child view controller matching current view controller view. + /// + /// - Parameter viewController: The child view controller to add. + func vc_addChildViewController(viewController: UIViewController) { + self.vc_addChildViewController(viewController: viewController, onView: self.view) + } + + + /// Add a child view controller on current view controller. + /// + /// - Parameters: + /// - viewController: The child view controller to add. + /// - view: The view on which to add the child view controller view. + func vc_addChildViewController(viewController: UIViewController, onView view: UIView) { + self.addChildViewController(viewController) + + viewController.view.frame = view.bounds + view.vc_addSubViewMatchingParent(viewController.view) + viewController.didMove(toParentViewController: self) + } + + + /// Remove a child view controller from current view controller. + /// + /// - Parameter viewController: The child view controller to remove. + func vc_removeChildViewController(viewController: UIViewController) { + viewController.willMove(toParentViewController: nil) + viewController.view.removeFromSuperview() + viewController.removeFromParentViewController() + } + + + /// Remove current view controller from parent. + func vc_removeFromParent() { + self.vc_removeChildViewController(viewController: self) + } +} diff --git a/Riot/Constants/RiotDesignValues.h b/Riot/Constants/RiotDesignValues.h deleted file mode 100644 index 35c4d05c8..000000000 --- a/Riot/Constants/RiotDesignValues.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2015 OpenMarket Ltd - Copyright 2017 Vector Creations 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 - -/** - Posted when the user interface theme has been changed. - */ -extern NSString *const kRiotDesignValuesDidChangeThemeNotification; - -/** - Convert a RGB hexadecimal value into a UIColor. - */ -#define UIColorFromRGB(rgbValue) \ - [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \ - green:((float)((rgbValue & 0x00FF00) >> 8))/255.0 \ - blue:((float)((rgbValue & 0x0000FF) >> 0))/255.0 \ - alpha:1.0] - -#pragma mark - Riot Theme Colors (depends on the selected theme light or dark). -extern UIColor *kRiotPrimaryBgColor; -extern UIColor *kRiotSecondaryBgColor; -extern UIColor *kRiotPrimaryTextColor; -extern UIColor *kRiotSecondaryTextColor; //subtitle, sending messages color. -extern UIColor *kRiotPlaceholderTextColor; // nil is used to keep the default color. -extern UIColor *kRiotTopicTextColor; -extern UIColor *kRiotSelectedBgColor; // nil is used to keep the default color. -extern UIColor *kRiotAuxiliaryColor; // kRiotColorSilver by default. -extern UIColor *kRiotOverlayColor; // fading behind dialog modals. This color includes the transparency value. -extern UIColor *kRiotKeyboardColor; - -#pragma mark - Riot Colors -extern UIColor *kRiotColorGreen; -extern UIColor *kRiotColorLightGreen; -extern UIColor *kRiotColorLightOrange; -extern UIColor *kRiotColorSilver; -extern UIColor *kRiotColorPinkRed; -extern UIColor *kRiotColorRed; -extern UIColor *kRiotColorIndigo; -extern UIColor *kRiotColorOrange; -extern UIColor *kRiotColorBlue; -extern UIColor *kRiotColorCuriousBlue; - -#pragma mark - Riot Standard Room Member Power Level -extern NSInteger const kRiotRoomModeratorLevel; -extern NSInteger const kRiotRoomAdminLevel; - -#pragma mark - Riot bar style -extern UIStatusBarStyle kRiotDesignStatusBarStyle; -extern UIBarStyle kRiotDesignSearchBarStyle; -extern UIColor *kRiotDesignSearchBarTintColor; - -extern UIKeyboardAppearance kRiotKeyboard; - -/** - `RiotDesignValues` class manages the Riot design parameters - */ -@interface RiotDesignValues : NSObject - -@end diff --git a/Riot/Constants/RiotDesignValues.m b/Riot/Constants/RiotDesignValues.m deleted file mode 100644 index a6d4ce672..000000000 --- a/Riot/Constants/RiotDesignValues.m +++ /dev/null @@ -1,233 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2017 Vector Creations 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 "RiotDesignValues.h" - -#ifdef IS_SHARE_EXTENSION -#import "RiotShareExtension-Swift.h" -#else -#import "Riot-Swift.h" -#endif - - -NSString *const kRiotDesignValuesDidChangeThemeNotification = @"kRiotDesignValuesDidChangeThemeNotification"; - -UIColor *kRiotPrimaryBgColor; -UIColor *kRiotSecondaryBgColor; -UIColor *kRiotPrimaryTextColor; -UIColor *kRiotSecondaryTextColor; -UIColor *kRiotPlaceholderTextColor; -UIColor *kRiotTopicTextColor; -UIColor *kRiotSelectedBgColor; -UIColor *kRiotAuxiliaryColor; -UIColor *kRiotOverlayColor; -UIColor *kRiotKeyboardColor; - -// Riot Colors -UIColor *kRiotColorGreen; -UIColor *kRiotColorLightGreen; -UIColor *kRiotColorLightOrange; -UIColor *kRiotColorSilver; -UIColor *kRiotColorPinkRed; -UIColor *kRiotColorRed; -UIColor *kRiotColorIndigo; -UIColor *kRiotColorOrange; -UIColor *kRiotColorBlue; -UIColor *kRiotColorCuriousBlue; - -// Riot Background Colors -UIColor *kRiotBgColorWhite; -UIColor *kRiotBgColorBlack; -UIColor *kRiotBgColorOLEDBlack; -UIColor *kRiotColorLightGrey; -UIColor *kRiotColorLightBlack; -UIColor *kRiotColorLightKeyboard; -UIColor *kRiotColorDarkKeyboard; - -// Riot Text Colors -UIColor *kRiotTextColorBlack; -UIColor *kRiotTextColorDarkGray; -UIColor *kRiotTextColorGray; -UIColor *kRiotTextColorWhite; -UIColor *kRiotTextColorDarkWhite; - -NSInteger const kRiotRoomModeratorLevel = 50; -NSInteger const kRiotRoomAdminLevel = 100; - -UIStatusBarStyle kRiotDesignStatusBarStyle = UIStatusBarStyleDefault; -UIBarStyle kRiotDesignSearchBarStyle = UIBarStyleDefault; -UIColor *kRiotDesignSearchBarTintColor = nil; - -UIKeyboardAppearance kRiotKeyboard; - -@implementation RiotDesignValues - -+ (RiotDesignValues *)sharedInstance -{ - static RiotDesignValues *sharedOnceInstance; - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedOnceInstance = [[RiotDesignValues alloc] init]; - }); - - return sharedOnceInstance; -} - -+ (void)load -{ - [super load]; - - // Load colors at the app load time for the life of the app - - // Colors as defined by the design - kRiotColorGreen = UIColorFromRGB(0x62CE9C); - kRiotColorSilver = UIColorFromRGB(0xC7C7CC); - kRiotColorPinkRed = UIColorFromRGB(0xFF0064); - kRiotColorRed = UIColorFromRGB(0xFF4444); - kRiotColorIndigo = UIColorFromRGB(0xBD79CC); - kRiotColorOrange = UIColorFromRGB(0xF8A15F); - kRiotColorBlue = UIColorFromRGB(0x81BDDB); - kRiotColorCuriousBlue = UIColorFromRGB(0x2A9EDB); - - kRiotBgColorWhite = [UIColor whiteColor]; - kRiotBgColorBlack = UIColorFromRGB(0x2D2D2D); - kRiotBgColorOLEDBlack = [UIColor blackColor]; - - kRiotColorLightGrey = UIColorFromRGB(0xF2F2F2); - kRiotColorLightBlack = UIColorFromRGB(0x353535); - - kRiotColorLightKeyboard = UIColorFromRGB(0xE7E7E7); - kRiotColorDarkKeyboard = UIColorFromRGB(0x7E7E7E); - - kRiotTextColorBlack = UIColorFromRGB(0x3C3C3C); - kRiotTextColorDarkGray = UIColorFromRGB(0x4A4A4A); - kRiotTextColorGray = UIColorFromRGB(0x9D9D9D); - kRiotTextColorWhite = UIColorFromRGB(0xDDDDDD); - kRiotTextColorDarkWhite = UIColorFromRGB(0xD9D9D9); - - // Colors copied from Vector web - kRiotColorLightGreen = UIColorFromRGB(0x50e2c2); - kRiotColorLightOrange = UIColorFromRGB(0xf4c371); - - // Observe user interface theme change. - [[NSUserDefaults standardUserDefaults] addObserver:[RiotDesignValues sharedInstance] forKeyPath:@"userInterfaceTheme" options:0 context:nil]; - [[RiotDesignValues sharedInstance] userInterfaceThemeDidChange]; - - // Observe "Invert Colours" settings changes (available since iOS 11) - [[NSNotificationCenter defaultCenter] addObserver:[RiotDesignValues sharedInstance] selector:@selector(accessibilityInvertColorsStatusDidChange) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([@"userInterfaceTheme" isEqualToString:keyPath]) - { - [self userInterfaceThemeDidChange]; - } -} - -- (void)accessibilityInvertColorsStatusDidChange -{ - // Refresh the theme only for "auto" - NSString *theme = RiotSettings.shared.userInterfaceTheme; - if (!theme || [theme isEqualToString:@"auto"]) - { - [self userInterfaceThemeDidChange]; - } -} - -- (void)userInterfaceThemeDidChange -{ - // Retrieve the current selected theme ("light" if none. "auto" is used as default from iOS 11). - NSString *theme = RiotSettings.shared.userInterfaceTheme; - - if (!theme || [theme isEqualToString:@"auto"]) - { - theme = UIAccessibilityIsInvertColorsEnabled() ? @"dark" : @"light"; - } - - if ([theme isEqualToString:@"dark"]) - { - // Set dark theme colors - kRiotPrimaryBgColor = kRiotBgColorBlack; - kRiotSecondaryBgColor = kRiotColorLightBlack; - kRiotPrimaryTextColor = kRiotTextColorWhite; - kRiotSecondaryTextColor = kRiotTextColorGray; - kRiotPlaceholderTextColor = [UIColor colorWithWhite:1.0 alpha:0.3]; - kRiotTopicTextColor = kRiotTextColorDarkWhite; - kRiotSelectedBgColor = [UIColor blackColor]; - - kRiotDesignStatusBarStyle = UIStatusBarStyleLightContent; - kRiotDesignSearchBarStyle = UIBarStyleBlack; - kRiotDesignSearchBarTintColor = kRiotColorGreen; - - kRiotAuxiliaryColor = kRiotTextColorGray; - kRiotOverlayColor = [UIColor colorWithWhite:0.3 alpha:0.5]; - kRiotKeyboardColor = kRiotColorDarkKeyboard; - - [UITextField appearance].keyboardAppearance = UIKeyboardAppearanceDark; - kRiotKeyboard = UIKeyboardAppearanceDark; - } - else if ([theme isEqualToString:@"black"]) - { - // Set black theme colors - kRiotPrimaryBgColor = kRiotBgColorOLEDBlack; - kRiotSecondaryBgColor = kRiotColorLightBlack; - kRiotPrimaryTextColor = kRiotTextColorWhite; - kRiotSecondaryTextColor = kRiotTextColorGray; - kRiotPlaceholderTextColor = [UIColor colorWithWhite:1.0 alpha:0.3]; - kRiotTopicTextColor = kRiotTextColorDarkWhite; - kRiotSelectedBgColor = [UIColor blackColor]; - - kRiotDesignStatusBarStyle = UIStatusBarStyleLightContent; - kRiotDesignSearchBarStyle = UIBarStyleBlack; - kRiotDesignSearchBarTintColor = kRiotColorGreen; - - kRiotAuxiliaryColor = kRiotTextColorGray; - kRiotOverlayColor = [UIColor colorWithWhite:0.3 alpha:0.5]; - kRiotKeyboardColor = kRiotColorDarkKeyboard; - - [UITextField appearance].keyboardAppearance = UIKeyboardAppearanceDark; - kRiotKeyboard = UIKeyboardAppearanceDark; - } - else - { - // Set light theme colors by default. - kRiotPrimaryBgColor = kRiotBgColorWhite; - kRiotSecondaryBgColor = kRiotColorLightGrey; - kRiotPrimaryTextColor = kRiotTextColorBlack; - kRiotSecondaryTextColor = kRiotTextColorGray; - kRiotPlaceholderTextColor = nil; // Use default 70% gray color. - kRiotTopicTextColor = kRiotTextColorDarkGray; - kRiotSelectedBgColor = nil; // Use the default selection color. - - kRiotDesignStatusBarStyle = UIStatusBarStyleDefault; - kRiotDesignSearchBarStyle = UIBarStyleDefault; - kRiotDesignSearchBarTintColor = nil; // Default tint color. - - kRiotAuxiliaryColor = kRiotColorSilver; - kRiotOverlayColor = [UIColor colorWithWhite:0.7 alpha:0.5]; - kRiotKeyboardColor = kRiotColorLightKeyboard; - - [UITextField appearance].keyboardAppearance = UIKeyboardAppearanceLight; - kRiotKeyboard = UIKeyboardAppearanceLight; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:kRiotDesignValuesDidChangeThemeNotification object:nil]; -} - -@end diff --git a/Riot/Coordinators/Coordinator.swift b/Riot/Coordinators/Coordinator.swift new file mode 100755 index 000000000..4d7f27b6a --- /dev/null +++ b/Riot/Coordinators/Coordinator.swift @@ -0,0 +1,51 @@ +/* + 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 + +/// Protocol describing a [Coordinator](http://khanlou.com/2015/10/coordinators-redux/). +/// Coordinators are the objects which control the navigation flow of the application. +/// It helps to isolate and reuse view controllers and pass dependencies down the navigation hierarchy. +protocol Coordinator: class { + + /// Starts job of the coordinator. + func start() + + /// Child coordinators to retain. Prevent them from getting deallocated. + var childCoordinators: [Coordinator] { get set } + + /// Stores coordinator to the `childCoordinators` array. + /// + /// - Parameter childCoordinator: Child coordinator to store. + func add(childCoordinator: Coordinator) + + /// Remove coordinator from the `childCoordinators` array. + /// + /// - Parameter childCoordinator: Child coordinator to remove. + func remove(childCoordinator: Coordinator) +} + +// `Coordinator` default implementation +extension Coordinator { + + func add(childCoordinator coordinator: Coordinator) { + childCoordinators.append(coordinator) + } + + func remove(childCoordinator: Coordinator) { + self.childCoordinators = self.childCoordinators.filter { $0 !== childCoordinator } + } +} diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift new file mode 100644 index 000000000..78ad0f5a7 --- /dev/null +++ b/Riot/Generated/Images.swift @@ -0,0 +1,202 @@ +// swiftlint:disable all +// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen + +#if os(OSX) + import AppKit.NSImage + internal typealias AssetColorTypeAlias = NSColor + internal typealias AssetImageTypeAlias = NSImage +#elseif os(iOS) || os(tvOS) || os(watchOS) + import UIKit.UIImage + internal typealias AssetColorTypeAlias = UIColor + internal typealias AssetImageTypeAlias = UIImage +#endif + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - Asset Catalogs + +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +internal enum Asset { + internal enum Images { + internal static let callAudioMuteOffIcon = ImageAsset(name: "call_audio_mute_off_icon") + internal static let callAudioMuteOnIcon = ImageAsset(name: "call_audio_mute_on_icon") + internal static let callChatIcon = ImageAsset(name: "call_chat_icon") + internal static let callHangupIcon = ImageAsset(name: "call_hangup_icon") + internal static let callSpeakerOffIcon = ImageAsset(name: "call_speaker_off_icon") + internal static let callSpeakerOnIcon = ImageAsset(name: "call_speaker_on_icon") + internal static let callVideoMuteOffIcon = ImageAsset(name: "call_video_mute_off_icon") + internal static let callVideoMuteOnIcon = ImageAsset(name: "call_video_mute_on_icon") + internal static let cameraSwitch = ImageAsset(name: "camera_switch") + internal static let riotIconCallkit = ImageAsset(name: "riot_icon_callkit") + internal static let adminIcon = ImageAsset(name: "admin_icon") + internal static let backIcon = ImageAsset(name: "back_icon") + internal static let chevron = ImageAsset(name: "chevron") + internal static let disclosureIcon = ImageAsset(name: "disclosure_icon") + internal static let group = ImageAsset(name: "group") + internal static let logo = ImageAsset(name: "logo") + internal static let placeholder = ImageAsset(name: "placeholder") + internal static let plusIcon = ImageAsset(name: "plus_icon") + internal static let removeIcon = ImageAsset(name: "remove_icon") + internal static let selectionTick = ImageAsset(name: "selection_tick") + internal static let selectionUntick = ImageAsset(name: "selection_untick") + internal static let shrinkIcon = ImageAsset(name: "shrink_icon") + internal static let startChat = ImageAsset(name: "start_chat") + internal static let addGroupParticipant = ImageAsset(name: "add_group_participant") + internal static let createGroup = ImageAsset(name: "create_group") + internal static let removeIconBlue = ImageAsset(name: "remove_icon_blue") + internal static let riotIcon = ImageAsset(name: "riot_icon") + internal static let e2eBlocked = ImageAsset(name: "e2e_blocked") + internal static let e2eUnencrypted = ImageAsset(name: "e2e_unencrypted") + internal static let e2eWarning = ImageAsset(name: "e2e_warning") + internal static let directChatOff = ImageAsset(name: "directChatOff") + internal static let directChatOn = ImageAsset(name: "directChatOn") + internal static let favourite = ImageAsset(name: "favourite") + internal static let favouriteOff = ImageAsset(name: "favouriteOff") + internal static let leave = ImageAsset(name: "leave") + internal static let notifications = ImageAsset(name: "notifications") + internal static let notificationsOff = ImageAsset(name: "notificationsOff") + internal static let priorityHigh = ImageAsset(name: "priorityHigh") + internal static let priorityLow = ImageAsset(name: "priorityLow") + internal static let createRoom = ImageAsset(name: "create_room") + internal static let closeBanner = ImageAsset(name: "close_banner") + internal static let importFilesButton = ImageAsset(name: "import_files_button") + internal static let keyBackupLogo = ImageAsset(name: "key_backup_logo") + internal static let revealPasswordButton = ImageAsset(name: "reveal_password_button") + internal static let launchScreenRiot = ImageAsset(name: "LaunchScreenRiot") + internal static let cameraCapture = ImageAsset(name: "camera_capture") + internal static let cameraPlay = ImageAsset(name: "camera_play") + internal static let cameraStop = ImageAsset(name: "camera_stop") + internal static let cameraVideoCapture = ImageAsset(name: "camera_video_capture") + internal static let videoIcon = ImageAsset(name: "video_icon") + internal static let createDirectChat = ImageAsset(name: "create_direct_chat") + internal static let error = ImageAsset(name: "error") + internal static let newmessages = ImageAsset(name: "newmessages") + internal static let scrolldown = ImageAsset(name: "scrolldown") + internal static let scrollup = ImageAsset(name: "scrollup") + internal static let typing = ImageAsset(name: "typing") + internal static let uploadIcon = ImageAsset(name: "upload_icon") + internal static let voiceCallIcon = ImageAsset(name: "voice_call_icon") + internal static let addParticipant = ImageAsset(name: "add_participant") + internal static let appsIcon = ImageAsset(name: "apps-icon") + internal static let detailsIcon = ImageAsset(name: "details_icon") + internal static let editIcon = ImageAsset(name: "edit_icon") + internal static let jumpToUnread = ImageAsset(name: "jump_to_unread") + internal static let mainAliasIcon = ImageAsset(name: "main_alias_icon") + internal static let membersListIcon = ImageAsset(name: "members_list_icon") + internal static let modIcon = ImageAsset(name: "mod_icon") + internal static let fileDocIcon = ImageAsset(name: "file_doc_icon") + internal static let fileMusicIcon = ImageAsset(name: "file_music_icon") + internal static let filePhotoIcon = ImageAsset(name: "file_photo_icon") + internal static let fileVideoIcon = ImageAsset(name: "file_video_icon") + internal static let searchBg = ImageAsset(name: "search_bg") + internal static let searchIcon = ImageAsset(name: "search_icon") + internal static let removeIconPink = ImageAsset(name: "remove_icon_pink") + internal static let settingsIcon = ImageAsset(name: "settings_icon") + internal static let tabFavourites = ImageAsset(name: "tab_favourites") + internal static let tabFavouritesSelected = ImageAsset(name: "tab_favourites_selected") + internal static let tabGroups = ImageAsset(name: "tab_groups") + internal static let tabGroupsSelected = ImageAsset(name: "tab_groups_selected") + internal static let tabHome = ImageAsset(name: "tab_home") + internal static let tabHomeSelected = ImageAsset(name: "tab_home_selected") + internal static let tabPeople = ImageAsset(name: "tab_people") + internal static let tabPeopleSelected = ImageAsset(name: "tab_people_selected") + internal static let tabRooms = ImageAsset(name: "tab_rooms") + internal static let tabRoomsSelected = ImageAsset(name: "tab_rooms_selected") + } + internal enum SharedImages { + internal static let animatedLogo0 = ImageAsset(name: "animatedLogo-0") + internal static let animatedLogo1 = ImageAsset(name: "animatedLogo-1") + internal static let animatedLogo2 = ImageAsset(name: "animatedLogo-2") + internal static let animatedLogo3 = ImageAsset(name: "animatedLogo-3") + internal static let animatedLogo4 = ImageAsset(name: "animatedLogo-4") + internal static let cancel = ImageAsset(name: "cancel") + internal static let e2eVerified = ImageAsset(name: "e2e_verified") + } +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details + +internal struct ColorAsset { + internal fileprivate(set) var name: String + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, OSX 10.13, *) + internal var color: AssetColorTypeAlias { + return AssetColorTypeAlias(asset: self) + } +} + +internal extension AssetColorTypeAlias { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, OSX 10.13, *) + convenience init!(asset: ColorAsset) { + let bundle = Bundle(for: BundleToken.self) + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(OSX) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +internal struct DataAsset { + internal fileprivate(set) var name: String + + #if os(iOS) || os(tvOS) || os(OSX) + @available(iOS 9.0, tvOS 9.0, OSX 10.11, *) + internal var data: NSDataAsset { + return NSDataAsset(asset: self) + } + #endif +} + +#if os(iOS) || os(tvOS) || os(OSX) +@available(iOS 9.0, tvOS 9.0, OSX 10.11, *) +internal extension NSDataAsset { + convenience init!(asset: DataAsset) { + let bundle = Bundle(for: BundleToken.self) + #if os(iOS) || os(tvOS) + self.init(name: asset.name, bundle: bundle) + #elseif os(OSX) + self.init(name: NSDataAsset.Name(asset.name), bundle: bundle) + #endif + } +} +#endif + +internal struct ImageAsset { + internal fileprivate(set) var name: String + + internal var image: AssetImageTypeAlias { + let bundle = Bundle(for: BundleToken.self) + #if os(iOS) || os(tvOS) + let image = AssetImageTypeAlias(named: name, in: bundle, compatibleWith: nil) + #elseif os(OSX) + let image = bundle.image(forResource: NSImage.Name(name)) + #elseif os(watchOS) + let image = AssetImageTypeAlias(named: name) + #endif + guard let result = image else { fatalError("Unable to load image named \(name).") } + return result + } +} + +internal extension AssetImageTypeAlias { + @available(iOS 1.0, tvOS 1.0, watchOS 1.0, *) + @available(OSX, deprecated, + message: "This initializer is unsafe on macOS, please use the ImageAsset.image property") + convenience init!(asset: ImageAsset) { + #if os(iOS) || os(tvOS) + let bundle = Bundle(for: BundleToken.self) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(OSX) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +private final class BundleToken {} diff --git a/Riot/Generated/RiotDefaults.swift b/Riot/Generated/RiotDefaults.swift new file mode 100644 index 000000000..66279954e --- /dev/null +++ b/Riot/Generated/RiotDefaults.swift @@ -0,0 +1,79 @@ +// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen + +import Foundation + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - Plist Files + +// swiftlint:disable identifier_name line_length type_body_length +internal enum RiotDefaults { + private static let _document = PlistDocument(path: "Riot-Defaults.plist") + + internal static let bugReportApp: String = _document["bugReportApp"] + internal static let bugReportEndpointUrl: String = _document["bugReportEndpointUrl"] + internal static let createConferenceCallsWithJitsi: Bool = _document["createConferenceCallsWithJitsi"] + internal static let enableRageShake: Bool = _document["enableRageShake"] + internal static let homeserver: String = _document["homeserver"] + internal static let homeserverurl: String = _document["homeserverurl"] + internal static let identityserverurl: String = _document["identityserverurl"] + internal static let integrationsRestUrl: String = _document["integrationsRestUrl"] + internal static let integrationsUiUrl: String = _document["integrationsUiUrl"] + internal static let matrixApps: Bool = _document["matrixApps"] + internal static let maxAllowedMediaCacheSize: Int = _document["maxAllowedMediaCacheSize"] + internal static let pinRoomsWithMissedNotif: Bool = _document["pinRoomsWithMissedNotif"] + internal static let pinRoomsWithUnread: Bool = _document["pinRoomsWithUnread"] + internal static let piwik: [String: Any] = _document["piwik"] + internal static let presenceColorForOfflineUser: Int = _document["presenceColorForOfflineUser"] + internal static let presenceColorForOnlineUser: Int = _document["presenceColorForOnlineUser"] + internal static let presenceColorForUnavailableUser: Int = _document["presenceColorForUnavailableUser"] + internal static let pushGatewayURL: String = _document["pushGatewayURL"] + internal static let pushKitAppIdProd: String = _document["pushKitAppIdProd"] + internal static let pusherAppIdDev: String = _document["pusherAppIdDev"] + internal static let pusherAppIdProd: String = _document["pusherAppIdProd"] + internal static let roomDirectoryServers: [String: Any] = _document["roomDirectoryServers"] + internal static let showAllEventsInRoomHistory: Bool = _document["showAllEventsInRoomHistory"] + internal static let showLeftMembersInRoomMemberList: Bool = _document["showLeftMembersInRoomMemberList"] + internal static let showRedactionsInRoomHistory: Bool = _document["showRedactionsInRoomHistory"] + internal static let showUnsupportedEventsInRoomHistory: Bool = _document["showUnsupportedEventsInRoomHistory"] + internal static let sortRoomMembersUsingLastSeenTime: Bool = _document["sortRoomMembersUsingLastSeenTime"] + internal static let syncLocalContacts: Bool = _document["syncLocalContacts"] + internal static let webAppUrl: String = _document["webAppUrl"] + internal static let webAppUrlBeta: String = _document["webAppUrlBeta"] + internal static let webAppUrlDev: String = _document["webAppUrlDev"] +} +// swiftlint:enable identifier_name line_length type_body_length + +// MARK: - Implementation Details + +private func arrayFromPlist(at path: String) -> [T] { + let bundle = Bundle(for: BundleToken.self) + guard let url = bundle.url(forResource: path, withExtension: nil), + let data = NSArray(contentsOf: url) as? [T] else { + fatalError("Unable to load PLIST at path: \(path)") + } + return data +} + +private struct PlistDocument { + let data: [String: Any] + + init(path: String) { + let bundle = Bundle(for: BundleToken.self) + guard let url = bundle.url(forResource: path, withExtension: nil), + let data = NSDictionary(contentsOf: url) as? [String: Any] else { + fatalError("Unable to load PLIST at path: \(path)") + } + self.data = data + } + + subscript(key: String) -> T { + guard let result = data[key] as? T else { + fatalError("Property '\(key)' is not of type \(T.self)") + } + return result + } +} + +private final class BundleToken {} diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift new file mode 100644 index 000000000..7b3025f9f --- /dev/null +++ b/Riot/Generated/Storyboards.swift @@ -0,0 +1,90 @@ +// swiftlint:disable all +// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen + +// swiftlint:disable sorted_imports +import Foundation +import UIKit + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - Storyboard Scenes + +// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name +internal enum StoryboardScene { + internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType { + internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupRecoverFromPassphraseViewController.self) + } + internal enum KeyBackupRecoverFromRecoveryKeyViewController: StoryboardType { + internal static let storyboardName = "KeyBackupRecoverFromRecoveryKeyViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupRecoverFromRecoveryKeyViewController.self) + } + internal enum KeyBackupRecoverSuccessViewController: StoryboardType { + internal static let storyboardName = "KeyBackupRecoverSuccessViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupRecoverSuccessViewController.self) + } + internal enum KeyBackupSetupIntroViewController: StoryboardType { + internal static let storyboardName = "KeyBackupSetupIntroViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupIntroViewController.self) + } + internal enum KeyBackupSetupPassphraseViewController: StoryboardType { + internal static let storyboardName = "KeyBackupSetupPassphraseViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupPassphraseViewController.self) + } + internal enum KeyBackupSetupSuccessFromPassphraseViewController: StoryboardType { + internal static let storyboardName = "KeyBackupSetupSuccessFromPassphraseViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromPassphraseViewController.self) + } + internal enum KeyBackupSetupSuccessFromRecoveryKeyViewController: StoryboardType { + internal static let storyboardName = "KeyBackupSetupSuccessFromRecoveryKeyViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self) + } +} +// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name + +// MARK: - Implementation Details + +internal protocol StoryboardType { + static var storyboardName: String { get } +} + +internal extension StoryboardType { + static var storyboard: UIStoryboard { + let name = self.storyboardName + return UIStoryboard(name: name, bundle: Bundle(for: BundleToken.self)) + } +} + +internal struct SceneType { + internal let storyboard: StoryboardType.Type + internal let identifier: String + + internal func instantiate() -> T { + let identifier = self.identifier + guard let controller = storyboard.storyboard.instantiateViewController(withIdentifier: identifier) as? T else { + fatalError("ViewController '\(identifier)' is not of the expected class \(T.self).") + } + return controller + } +} + +internal struct InitialSceneType { + internal let storyboard: StoryboardType.Type + + internal func instantiate() -> T { + guard let controller = storyboard.storyboard.instantiateInitialViewController() as? T else { + fatalError("ViewController is not of the expected class \(T.self).") + } + return controller + } +} + +private final class BundleToken {} diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift new file mode 100644 index 000000000..c13db1340 --- /dev/null +++ b/Riot/Generated/Strings.swift @@ -0,0 +1,2490 @@ +// swiftlint:disable all +// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen + +import Foundation + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - Strings + +// swiftlint:disable function_parameter_count identifier_name line_length type_body_length +internal enum VectorL10n { + /// Accept + internal static var accept: String { + return VectorL10n.tr("Vector", "accept") + } + /// Logout all accounts + internal static var accountLogoutAll: String { + return VectorL10n.tr("Vector", "account_logout_all") + } + /// Active Call + internal static var activeCall: String { + return VectorL10n.tr("Vector", "active_call") + } + /// Active Call (%@) + internal static func activeCallDetails(_ p1: String) -> String { + return VectorL10n.tr("Vector", "active_call_details", p1) + } + /// Please review and accept the policies of this homeserver: + internal static var authAcceptPolicies: String { + return VectorL10n.tr("Vector", "auth_accept_policies") + } + /// Add an email address and a phone number to your account to let users discover you. Email address will also let you reset your password. + internal static var authAddEmailAndPhoneMessage: String { + return VectorL10n.tr("Vector", "auth_add_email_and_phone_message") + } + /// Registration with email and phone number at once is not supported yet until the api exists. Only the phone number will be taken into account. You may add your email to your profile in settings. + internal static var authAddEmailAndPhoneWarning: String { + return VectorL10n.tr("Vector", "auth_add_email_and_phone_warning") + } + /// Add an email address to your account to let users discover you, and let you reset password. + internal static var authAddEmailMessage: String { + return VectorL10n.tr("Vector", "auth_add_email_message") + } + /// Add an email address and/or a phone number to your account to let users discover you. Email address will also let you reset your password. + internal static var authAddEmailPhoneMessage: String { + return VectorL10n.tr("Vector", "auth_add_email_phone_message") + } + /// Add a phone number to your account to let users discover you. + internal static var authAddPhoneMessage: String { + return VectorL10n.tr("Vector", "auth_add_phone_message") + } + /// This email address is already in use + internal static var authEmailInUse: String { + return VectorL10n.tr("Vector", "auth_email_in_use") + } + /// Failed to send email: This email address was not found + internal static var authEmailNotFound: String { + return VectorL10n.tr("Vector", "auth_email_not_found") + } + /// Email address + internal static var authEmailPlaceholder: String { + return VectorL10n.tr("Vector", "auth_email_placeholder") + } + /// Please check your email to continue registration + internal static var authEmailValidationMessage: String { + return VectorL10n.tr("Vector", "auth_email_validation_message") + } + /// Forgot password? + internal static var authForgotPassword: String { + return VectorL10n.tr("Vector", "auth_forgot_password") + } + /// URL (e.g. https://matrix.org) + internal static var authHomeServerPlaceholder: String { + return VectorL10n.tr("Vector", "auth_home_server_placeholder") + } + /// URL (e.g. https://matrix.org) + internal static var authIdentityServerPlaceholder: String { + return VectorL10n.tr("Vector", "auth_identity_server_placeholder") + } + /// This doesn't look like a valid email address + internal static var authInvalidEmail: String { + return VectorL10n.tr("Vector", "auth_invalid_email") + } + /// Incorrect username and/or password + internal static var authInvalidLoginParam: String { + return VectorL10n.tr("Vector", "auth_invalid_login_param") + } + /// Password too short (min 6) + internal static var authInvalidPassword: String { + return VectorL10n.tr("Vector", "auth_invalid_password") + } + /// This doesn't look like a valid phone number + internal static var authInvalidPhone: String { + return VectorL10n.tr("Vector", "auth_invalid_phone") + } + /// User names may only contain letters, numbers, dots, hyphens and underscores + internal static var authInvalidUserName: String { + return VectorL10n.tr("Vector", "auth_invalid_user_name") + } + /// Log in + internal static var authLogin: String { + return VectorL10n.tr("Vector", "auth_login") + } + /// Missing email address + internal static var authMissingEmail: String { + return VectorL10n.tr("Vector", "auth_missing_email") + } + /// Missing email address or phone number + internal static var authMissingEmailOrPhone: String { + return VectorL10n.tr("Vector", "auth_missing_email_or_phone") + } + /// Missing password + internal static var authMissingPassword: String { + return VectorL10n.tr("Vector", "auth_missing_password") + } + /// Missing phone number + internal static var authMissingPhone: String { + return VectorL10n.tr("Vector", "auth_missing_phone") + } + /// Unable to verify phone number. + internal static var authMsisdnValidationError: String { + return VectorL10n.tr("Vector", "auth_msisdn_validation_error") + } + /// We've sent an SMS with an activation code. Please enter this code below. + internal static var authMsisdnValidationMessage: String { + return VectorL10n.tr("Vector", "auth_msisdn_validation_message") + } + /// Verification Pending + internal static var authMsisdnValidationTitle: String { + return VectorL10n.tr("Vector", "auth_msisdn_validation_title") + } + /// New password + internal static var authNewPasswordPlaceholder: String { + return VectorL10n.tr("Vector", "auth_new_password_placeholder") + } + /// Email address (optional) + internal static var authOptionalEmailPlaceholder: String { + return VectorL10n.tr("Vector", "auth_optional_email_placeholder") + } + /// Phone number (optional) + internal static var authOptionalPhonePlaceholder: String { + return VectorL10n.tr("Vector", "auth_optional_phone_placeholder") + } + /// Passwords don't match + internal static var authPasswordDontMatch: String { + return VectorL10n.tr("Vector", "auth_password_dont_match") + } + /// Password + internal static var authPasswordPlaceholder: String { + return VectorL10n.tr("Vector", "auth_password_placeholder") + } + /// This phone number is already in use + internal static var authPhoneInUse: String { + return VectorL10n.tr("Vector", "auth_phone_in_use") + } + /// Phone number + internal static var authPhonePlaceholder: String { + return VectorL10n.tr("Vector", "auth_phone_placeholder") + } + /// This Home Server would like to make sure you are not a robot + internal static var authRecaptchaMessage: String { + return VectorL10n.tr("Vector", "auth_recaptcha_message") + } + /// Register + internal static var authRegister: String { + return VectorL10n.tr("Vector", "auth_register") + } + /// Confirm your new password + internal static var authRepeatNewPasswordPlaceholder: String { + return VectorL10n.tr("Vector", "auth_repeat_new_password_placeholder") + } + /// Repeat password + internal static var authRepeatPasswordPlaceholder: String { + return VectorL10n.tr("Vector", "auth_repeat_password_placeholder") + } + /// An email has been sent to %@. Once you've followed the link it contains, click below. + internal static func authResetPasswordEmailValidationMessage(_ p1: String) -> String { + return VectorL10n.tr("Vector", "auth_reset_password_email_validation_message", p1) + } + /// Your email address does not appear to be associated with a Matrix ID on this Homeserver. + internal static var authResetPasswordErrorNotFound: String { + return VectorL10n.tr("Vector", "auth_reset_password_error_not_found") + } + /// Failed to verify email address: make sure you clicked the link in the email + internal static var authResetPasswordErrorUnauthorized: String { + return VectorL10n.tr("Vector", "auth_reset_password_error_unauthorized") + } + /// To reset your password, enter the email address linked to your account: + internal static var authResetPasswordMessage: String { + return VectorL10n.tr("Vector", "auth_reset_password_message") + } + /// The email address linked to your account must be entered. + internal static var authResetPasswordMissingEmail: String { + return VectorL10n.tr("Vector", "auth_reset_password_missing_email") + } + /// A new password must be entered. + internal static var authResetPasswordMissingPassword: String { + return VectorL10n.tr("Vector", "auth_reset_password_missing_password") + } + /// I have verified my email address + internal static var authResetPasswordNextStepButton: String { + return VectorL10n.tr("Vector", "auth_reset_password_next_step_button") + } + /// Your password has been reset.\n\nYou have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, re-log in on each device. + internal static var authResetPasswordSuccessMessage: String { + return VectorL10n.tr("Vector", "auth_reset_password_success_message") + } + /// Return to login screen + internal static var authReturnToLogin: String { + return VectorL10n.tr("Vector", "auth_return_to_login") + } + /// Send Reset Email + internal static var authSendResetEmail: String { + return VectorL10n.tr("Vector", "auth_send_reset_email") + } + /// Skip + internal static var authSkip: String { + return VectorL10n.tr("Vector", "auth_skip") + } + /// Submit + internal static var authSubmit: String { + return VectorL10n.tr("Vector", "auth_submit") + } + /// The identity server is not trusted + internal static var authUntrustedIdServer: String { + return VectorL10n.tr("Vector", "auth_untrusted_id_server") + } + /// Use custom server options (advanced) + internal static var authUseServerOptions: String { + return VectorL10n.tr("Vector", "auth_use_server_options") + } + /// Email or user name + internal static var authUserIdPlaceholder: String { + return VectorL10n.tr("Vector", "auth_user_id_placeholder") + } + /// User name + internal static var authUserNamePlaceholder: String { + return VectorL10n.tr("Vector", "auth_user_name_placeholder") + } + /// Username in use + internal static var authUsernameInUse: String { + return VectorL10n.tr("Vector", "auth_username_in_use") + } + /// Back + internal static var back: String { + return VectorL10n.tr("Vector", "back") + } + /// Please describe what you did before the crash: + internal static var bugCrashReportDescription: String { + return VectorL10n.tr("Vector", "bug_crash_report_description") + } + /// Crash Report + internal static var bugCrashReportTitle: String { + return VectorL10n.tr("Vector", "bug_crash_report_title") + } + /// Please describe the bug. What did you do? What did you expect to happen? What actually happened? + internal static var bugReportDescription: String { + return VectorL10n.tr("Vector", "bug_report_description") + } + /// In order to diagnose problems, logs from this client will be sent with this bug report. If you would prefer to only send the text above, please untick: + internal static var bugReportLogsDescription: String { + return VectorL10n.tr("Vector", "bug_report_logs_description") + } + /// Uploading report + internal static var bugReportProgressUploading: String { + return VectorL10n.tr("Vector", "bug_report_progress_uploading") + } + /// Collecting logs + internal static var bugReportProgressZipping: String { + return VectorL10n.tr("Vector", "bug_report_progress_zipping") + } + /// The application has crashed last time. Would you like to submit a crash report? + internal static var bugReportPrompt: String { + return VectorL10n.tr("Vector", "bug_report_prompt") + } + /// Send + internal static var bugReportSend: String { + return VectorL10n.tr("Vector", "bug_report_send") + } + /// Send logs + internal static var bugReportSendLogs: String { + return VectorL10n.tr("Vector", "bug_report_send_logs") + } + /// Send screenshot + internal static var bugReportSendScreenshot: String { + return VectorL10n.tr("Vector", "bug_report_send_screenshot") + } + /// Bug Report + internal static var bugReportTitle: String { + return VectorL10n.tr("Vector", "bug_report_title") + } + /// There is already a call in progress. + internal static var callAlreadyDisplayed: String { + return VectorL10n.tr("Vector", "call_already_displayed") + } + /// Incoming video call... + internal static var callIncomingVideo: String { + return VectorL10n.tr("Vector", "call_incoming_video") + } + /// Incoming video call from %@ + internal static func callIncomingVideoPrompt(_ p1: String) -> String { + return VectorL10n.tr("Vector", "call_incoming_video_prompt", p1) + } + /// Incoming call... + internal static var callIncomingVoice: String { + return VectorL10n.tr("Vector", "call_incoming_voice") + } + /// Incoming voice call from %@ + internal static func callIncomingVoicePrompt(_ p1: String) -> String { + return VectorL10n.tr("Vector", "call_incoming_voice_prompt", p1) + } + /// Failed to join the conference call. + internal static var callJitsiError: String { + return VectorL10n.tr("Vector", "call_jitsi_error") + } + /// Camera + internal static var camera: String { + return VectorL10n.tr("Vector", "camera") + } + /// %@ doesn't have permission to use Camera, please change privacy settings + internal static func cameraAccessNotGranted(_ p1: String) -> String { + return VectorL10n.tr("Vector", "camera_access_not_granted", p1) + } + /// Cancel + internal static var cancel: String { + return VectorL10n.tr("Vector", "cancel") + } + /// collapse + internal static var collapse: String { + return VectorL10n.tr("Vector", "collapse") + } + /// Matrix users only + internal static var contactsAddressBookMatrixUsersToggle: String { + return VectorL10n.tr("Vector", "contacts_address_book_matrix_users_toggle") + } + /// No local contacts + internal static var contactsAddressBookNoContact: String { + return VectorL10n.tr("Vector", "contacts_address_book_no_contact") + } + /// You didn't allow Riot to access your local contacts + internal static var contactsAddressBookPermissionDenied: String { + return VectorL10n.tr("Vector", "contacts_address_book_permission_denied") + } + /// Permission required to access local contacts + internal static var contactsAddressBookPermissionRequired: String { + return VectorL10n.tr("Vector", "contacts_address_book_permission_required") + } + /// LOCAL CONTACTS + internal static var contactsAddressBookSection: String { + return VectorL10n.tr("Vector", "contacts_address_book_section") + } + /// USER DIRECTORY (offline) + internal static var contactsUserDirectoryOfflineSection: String { + return VectorL10n.tr("Vector", "contacts_user_directory_offline_section") + } + /// USER DIRECTORY + internal static var contactsUserDirectorySection: String { + return VectorL10n.tr("Vector", "contacts_user_directory_section") + } + /// Continue + internal static var `continue`: String { + return VectorL10n.tr("Vector", "continue") + } + /// Create + internal static var create: String { + return VectorL10n.tr("Vector", "create") + } + /// Please forget all messages I have sent when my account is deactivated ( + internal static var deactivateAccountForgetMessagesInformationPart1: String { + return VectorL10n.tr("Vector", "deactivate_account_forget_messages_information_part1") + } + /// Warning + internal static var deactivateAccountForgetMessagesInformationPart2Emphasize: String { + return VectorL10n.tr("Vector", "deactivate_account_forget_messages_information_part2_emphasize") + } + /// : this will cause future users to see an incomplete view of conversations) + internal static var deactivateAccountForgetMessagesInformationPart3: String { + return VectorL10n.tr("Vector", "deactivate_account_forget_messages_information_part3") + } + /// This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. + internal static var deactivateAccountInformationsPart1: String { + return VectorL10n.tr("Vector", "deactivate_account_informations_part1") + } + /// This action is irreversible. + internal static var deactivateAccountInformationsPart2Emphasize: String { + return VectorL10n.tr("Vector", "deactivate_account_informations_part2_emphasize") + } + /// \n\nDeactivating your account + internal static var deactivateAccountInformationsPart3: String { + return VectorL10n.tr("Vector", "deactivate_account_informations_part3") + } + /// does not by default cause us to forget messages you have sent. + internal static var deactivateAccountInformationsPart4Emphasize: String { + return VectorL10n.tr("Vector", "deactivate_account_informations_part4_emphasize") + } + /// If you would like us to forget your messages, please tick the box below\n\nMessage visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy. + internal static var deactivateAccountInformationsPart5: String { + return VectorL10n.tr("Vector", "deactivate_account_informations_part5") + } + /// To continue, please enter your password + internal static var deactivateAccountPasswordAlertMessage: String { + return VectorL10n.tr("Vector", "deactivate_account_password_alert_message") + } + /// Deactivate Account + internal static var deactivateAccountPasswordAlertTitle: String { + return VectorL10n.tr("Vector", "deactivate_account_password_alert_title") + } + /// Deactivate Account + internal static var deactivateAccountTitle: String { + return VectorL10n.tr("Vector", "deactivate_account_title") + } + /// Deactivate account + internal static var deactivateAccountValidateAction: String { + return VectorL10n.tr("Vector", "deactivate_account_validate_action") + } + /// Decline + internal static var decline: String { + return VectorL10n.tr("Vector", "decline") + } + /// %tu rooms + internal static func directoryCellDescription(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "directory_cell_description", p1) + } + /// Browse directory + internal static var directoryCellTitle: String { + return VectorL10n.tr("Vector", "directory_cell_title") + } + /// Failed to fetch data + internal static var directorySearchFail: String { + return VectorL10n.tr("Vector", "directory_search_fail") + } + /// %tu results found for %@ + internal static func directorySearchResults(_ p1: Int, _ p2: String) -> String { + return VectorL10n.tr("Vector", "directory_search_results", p1, p2) + } + /// >%tu results found for %@ + internal static func directorySearchResultsMoreThan(_ p1: Int, _ p2: String) -> String { + return VectorL10n.tr("Vector", "directory_search_results_more_than", p1, p2) + } + /// Browse directory results + internal static var directorySearchResultsTitle: String { + return VectorL10n.tr("Vector", "directory_search_results_title") + } + /// Searching directory… + internal static var directorySearchingTitle: String { + return VectorL10n.tr("Vector", "directory_searching_title") + } + /// All native Matrix rooms + internal static var directoryServerAllNativeRooms: String { + return VectorL10n.tr("Vector", "directory_server_all_native_rooms") + } + /// All rooms on %@ server + internal static func directoryServerAllRooms(_ p1: String) -> String { + return VectorL10n.tr("Vector", "directory_server_all_rooms", p1) + } + /// Select a directory + internal static var directoryServerPickerTitle: String { + return VectorL10n.tr("Vector", "directory_server_picker_title") + } + /// matrix.org + internal static var directoryServerPlaceholder: String { + return VectorL10n.tr("Vector", "directory_server_placeholder") + } + /// Type a homeserver to list public rooms from + internal static var directoryServerTypeHomeserver: String { + return VectorL10n.tr("Vector", "directory_server_type_homeserver") + } + /// Directory + internal static var directoryTitle: String { + return VectorL10n.tr("Vector", "directory_title") + } + /// Do not ask again + internal static var doNotAskAgain: String { + return VectorL10n.tr("Vector", "do_not_ask_again") + } + /// Riot now supports end-to-end encryption but you need to log in again to enable it.\n\nYou can do it now or later from the application settings. + internal static var e2eEnablingOnAppUpdate: String { + return VectorL10n.tr("Vector", "e2e_enabling_on_app_update") + } + /// A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new passphrase in Settings. + internal static var e2eKeyBackupWrongVersion: String { + return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version") + } + /// Settings + internal static var e2eKeyBackupWrongVersionButtonSettings: String { + return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version_button_settings") + } + /// It was me + internal static var e2eKeyBackupWrongVersionButtonWasme: String { + return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version_button_wasme") + } + /// New Key Backup + internal static var e2eKeyBackupWrongVersionTitle: String { + return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version_title") + } + /// You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver.\nThis is a once off; sorry for the inconvenience. + internal static var e2eNeedLogInAgain: String { + return VectorL10n.tr("Vector", "e2e_need_log_in_again") + } + /// Ignore request + internal static var e2eRoomKeyRequestIgnoreRequest: String { + return VectorL10n.tr("Vector", "e2e_room_key_request_ignore_request") + } + /// Your unverified device '%@' is requesting encryption keys. + internal static func e2eRoomKeyRequestMessage(_ p1: String) -> String { + return VectorL10n.tr("Vector", "e2e_room_key_request_message", p1) + } + /// You added a new device '%@', which is requesting encryption keys. + internal static func e2eRoomKeyRequestMessageNewDevice(_ p1: String) -> String { + return VectorL10n.tr("Vector", "e2e_room_key_request_message_new_device", p1) + } + /// Share without verifying + internal static var e2eRoomKeyRequestShareWithoutVerifying: String { + return VectorL10n.tr("Vector", "e2e_room_key_request_share_without_verifying") + } + /// Start verification... + internal static var e2eRoomKeyRequestStartVerification: String { + return VectorL10n.tr("Vector", "e2e_room_key_request_start_verification") + } + /// Encryption key request + internal static var e2eRoomKeyRequestTitle: String { + return VectorL10n.tr("Vector", "e2e_room_key_request_title") + } + /// Send an encrypted message… + internal static var encryptedRoomMessagePlaceholder: String { + return VectorL10n.tr("Vector", "encrypted_room_message_placeholder") + } + /// Send an encrypted reply… + internal static var encryptedRoomMessageReplyToPlaceholder: String { + return VectorL10n.tr("Vector", "encrypted_room_message_reply_to_placeholder") + } + /// VoIP conference added by %@ + internal static func eventFormatterJitsiWidgetAdded(_ p1: String) -> String { + return VectorL10n.tr("Vector", "event_formatter_jitsi_widget_added", p1) + } + /// VoIP conference removed by %@ + internal static func eventFormatterJitsiWidgetRemoved(_ p1: String) -> String { + return VectorL10n.tr("Vector", "event_formatter_jitsi_widget_removed", p1) + } + /// %tu membership changes + internal static func eventFormatterMemberUpdates(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "event_formatter_member_updates", p1) + } + /// Re-request encryption keys + internal static var eventFormatterRerequestKeysPart1Link: String { + return VectorL10n.tr("Vector", "event_formatter_rerequest_keys_part1_link") + } + /// from your other devices. + internal static var eventFormatterRerequestKeysPart2: String { + return VectorL10n.tr("Vector", "event_formatter_rerequest_keys_part2") + } + /// %@ widget added by %@ + internal static func eventFormatterWidgetAdded(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "event_formatter_widget_added", p1, p2) + } + /// %@ widget removed by %@ + internal static func eventFormatterWidgetRemoved(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "event_formatter_widget_removed", p1, p2) + } + /// To continue using the %@ homeserver you must review and agree to the terms and conditions. + internal static func gdprConsentNotGivenAlertMessage(_ p1: String) -> String { + return VectorL10n.tr("Vector", "gdpr_consent_not_given_alert_message", p1) + } + /// Review now + internal static var gdprConsentNotGivenAlertReviewNowAction: String { + return VectorL10n.tr("Vector", "gdpr_consent_not_given_alert_review_now_action") + } + /// Would you like to help improve %@ by automatically reporting anonymous crash reports and usage data? + internal static func googleAnalyticsUsePrompt(_ p1: String) -> String { + return VectorL10n.tr("Vector", "google_analytics_use_prompt", p1) + } + /// Home + internal static var groupDetailsHome: String { + return VectorL10n.tr("Vector", "group_details_home") + } + /// People + internal static var groupDetailsPeople: String { + return VectorL10n.tr("Vector", "group_details_people") + } + /// Rooms + internal static var groupDetailsRooms: String { + return VectorL10n.tr("Vector", "group_details_rooms") + } + /// Community Details + internal static var groupDetailsTitle: String { + return VectorL10n.tr("Vector", "group_details_title") + } + /// %tu members + internal static func groupHomeMultiMembersFormat(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "group_home_multi_members_format", p1) + } + /// %tu rooms + internal static func groupHomeMultiRoomsFormat(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "group_home_multi_rooms_format", p1) + } + /// 1 member + internal static var groupHomeOneMemberFormat: String { + return VectorL10n.tr("Vector", "group_home_one_member_format") + } + /// 1 room + internal static var groupHomeOneRoomFormat: String { + return VectorL10n.tr("Vector", "group_home_one_room_format") + } + /// %@ has invited you to join this community + internal static func groupInvitationFormat(_ p1: String) -> String { + return VectorL10n.tr("Vector", "group_invitation_format", p1) + } + /// INVITES + internal static var groupInviteSection: String { + return VectorL10n.tr("Vector", "group_invite_section") + } + /// Add participant + internal static var groupParticipantsAddParticipant: String { + return VectorL10n.tr("Vector", "group_participants_add_participant") + } + /// Filter community members + internal static var groupParticipantsFilterMembers: String { + return VectorL10n.tr("Vector", "group_participants_filter_members") + } + /// Search / invite by User ID or Name + internal static var groupParticipantsInviteAnotherUser: String { + return VectorL10n.tr("Vector", "group_participants_invite_another_user") + } + /// Malformed ID. Should be a Matrix ID like '@localpart:domain' + internal static var groupParticipantsInviteMalformedId: String { + return VectorL10n.tr("Vector", "group_participants_invite_malformed_id") + } + /// Invite Error + internal static var groupParticipantsInviteMalformedIdTitle: String { + return VectorL10n.tr("Vector", "group_participants_invite_malformed_id_title") + } + /// Are you sure you want to invite %@ to this group? + internal static func groupParticipantsInvitePromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "group_participants_invite_prompt_msg", p1) + } + /// Confirmation + internal static var groupParticipantsInvitePromptTitle: String { + return VectorL10n.tr("Vector", "group_participants_invite_prompt_title") + } + /// INVITED + internal static var groupParticipantsInvitedSection: String { + return VectorL10n.tr("Vector", "group_participants_invited_section") + } + /// Are you sure you want to leave the group? + internal static var groupParticipantsLeavePromptMsg: String { + return VectorL10n.tr("Vector", "group_participants_leave_prompt_msg") + } + /// Leave group + internal static var groupParticipantsLeavePromptTitle: String { + return VectorL10n.tr("Vector", "group_participants_leave_prompt_title") + } + /// Are you sure you want to remove %@ from this group? + internal static func groupParticipantsRemovePromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "group_participants_remove_prompt_msg", p1) + } + /// Confirmation + internal static var groupParticipantsRemovePromptTitle: String { + return VectorL10n.tr("Vector", "group_participants_remove_prompt_title") + } + /// Filter community rooms + internal static var groupRoomsFilterRooms: String { + return VectorL10n.tr("Vector", "group_rooms_filter_rooms") + } + /// COMMUNITIES + internal static var groupSection: String { + return VectorL10n.tr("Vector", "group_section") + } + /// Could not connect to the homeserver. + internal static var homeserverConnectionLost: String { + return VectorL10n.tr("Vector", "homeserver_connection_lost") + } + /// Invite + internal static var invite: String { + return VectorL10n.tr("Vector", "invite") + } + /// Join + internal static var join: String { + return VectorL10n.tr("Vector", "join") + } + /// Use Key Backup + internal static var keyBackupRecoverBannerSubtitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_banner_subtitle") + } + /// Never lose encrypted messages + internal static var keyBackupRecoverBannerTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_banner_title") + } + /// Done + internal static var keyBackupRecoverDoneAction: String { + return VectorL10n.tr("Vector", "key_backup_recover_done_action") + } + /// Use your recovery passphrase to unlock your secure message history + internal static var keyBackupRecoverFromPassphraseInfo: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_info") + } + /// Don’t know your recovery passphrase? You can + internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart1: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part1") + } + /// use your recovery key + internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart2: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part2") + } + /// . + internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart3: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part3") + } + /// Enter Passphrase + internal static var keyBackupRecoverFromPassphrasePassphrasePlaceholder: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_passphrase_placeholder") + } + /// Enter + internal static var keyBackupRecoverFromPassphrasePassphraseTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_passphrase_title") + } + /// Unlock History + internal static var keyBackupRecoverFromPassphraseRecoverAction: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_recover_action") + } + /// Use your recovery key to unlock your secure message history + internal static var keyBackupRecoverFromRecoveryKeyInfo: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_info") + } + /// Lost your recovery key? You can set up a new one in settings. + internal static var keyBackupRecoverFromRecoveryKeyLostRecoveryKeyAction: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_lost_recovery_key_action") + } + /// Unlock History + internal static var keyBackupRecoverFromRecoveryKeyRecoverAction: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recover_action") + } + /// Enter Recovery Key + internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyPlaceholder: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_placeholder") + } + /// Enter + internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_title") + } + /// Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase. + internal static var keyBackupRecoverInvalidPassphrase: String { + return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase") + } + /// Incorrect Recovery Passphrase + internal static var keyBackupRecoverInvalidPassphraseTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase_title") + } + /// Backup could not be decrypted with this key: please verify that you entered the correct recovery key. + internal static var keyBackupRecoverInvalidRecoveryKey: String { + return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key") + } + /// Recovery Key Mismatch + internal static var keyBackupRecoverInvalidRecoveryKeyTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key_title") + } + /// Backup Restored! + internal static var keyBackupRecoverSuccessInfo: String { + return VectorL10n.tr("Vector", "key_backup_recover_success_info") + } + /// Secure Messages + internal static var keyBackupRecoverTitle: String { + return VectorL10n.tr("Vector", "key_backup_recover_title") + } + /// Start using Key Backup + internal static var keyBackupSetupBannerSubtitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_banner_subtitle") + } + /// Never lose encrypted messages + internal static var keyBackupSetupBannerTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_banner_title") + } + /// Messages in encrypted rooms are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.\n\nSecurely back up your keys to avoid losing them. + internal static var keyBackupSetupIntroInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_info") + } + /// Manually export keys + internal static var keyBackupSetupIntroManualExportAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_manual_export_action") + } + /// (Advanced) + internal static var keyBackupSetupIntroManualExportInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_manual_export_info") + } + /// Use Key Backup + internal static var keyBackupSetupIntroSetupActionWithExistingBackup: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_setup_action_with_existing_backup") + } + /// Start using Key Backup + internal static var keyBackupSetupIntroSetupActionWithoutExistingBackup: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_setup_action_without_existing_backup") + } + /// Never lose encrypted messages + internal static var keyBackupSetupIntroTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_intro_title") + } + /// Passphrase doesn’t match + internal static var keyBackupSetupPassphraseConfirmPassphraseInvalid: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_invalid") + } + /// Confirm passphrase + internal static var keyBackupSetupPassphraseConfirmPassphrasePlaceholder: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_placeholder") + } + /// Confirm + internal static var keyBackupSetupPassphraseConfirmPassphraseTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_title") + } + /// Great! + internal static var keyBackupSetupPassphraseConfirmPassphraseValid: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_valid") + } + /// We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password. + internal static var keyBackupSetupPassphraseInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_info") + } + /// Try adding a word + internal static var keyBackupSetupPassphrasePassphraseInvalid: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_invalid") + } + /// Enter passphrase + internal static var keyBackupSetupPassphrasePassphrasePlaceholder: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_placeholder") + } + /// Enter + internal static var keyBackupSetupPassphrasePassphraseTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_title") + } + /// Great! + internal static var keyBackupSetupPassphrasePassphraseValid: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_valid") + } + /// Set Passphrase + internal static var keyBackupSetupPassphraseSetPassphraseAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_set_passphrase_action") + } + /// (Advanced) Set up with Recovery Key + internal static var keyBackupSetupPassphraseSetupRecoveryKeyAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_action") + } + /// Or, secure your backup with a Recovery Key, saving it somewhere safe. + internal static var keyBackupSetupPassphraseSetupRecoveryKeyInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_info") + } + /// Secure your backup with a Passphrase + internal static var keyBackupSetupPassphraseTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_passphrase_title") + } + /// You may lose secure messages if you log out or lose your device. + internal static var keyBackupSetupSkipAlertMessage: String { + return VectorL10n.tr("Vector", "key_backup_setup_skip_alert_message") + } + /// Skip + internal static var keyBackupSetupSkipAlertSkipAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_skip_alert_skip_action") + } + /// Are you sure? + internal static var keyBackupSetupSkipAlertTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_skip_alert_title") + } + /// Done + internal static var keyBackupSetupSuccessFromPassphraseDoneAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_done_action") + } + /// Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe). + internal static var keyBackupSetupSuccessFromPassphraseInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_info") + } + /// Save Recovery Key + internal static var keyBackupSetupSuccessFromPassphraseSaveRecoveryKeyAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_save_recovery_key_action") + } + /// Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe. + internal static var keyBackupSetupSuccessFromRecoveryKeyInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_info") + } + /// I've made a copy + internal static var keyBackupSetupSuccessFromRecoveryKeyMadeCopyAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_made_copy_action") + } + /// Make a Copy + internal static var keyBackupSetupSuccessFromRecoveryKeyMakeCopyAction: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_make_copy_action") + } + /// Recovery Key + internal static var keyBackupSetupSuccessFromRecoveryKeyRecoveryKeyTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_recovery_key_title") + } + /// Success! + internal static var keyBackupSetupSuccessTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_title") + } + /// Key Backup + internal static var keyBackupSetupTitle: String { + return VectorL10n.tr("Vector", "key_backup_setup_title") + } + /// %.1fK + internal static func largeBadgeValueKFormat(_ p1: Float) -> String { + return VectorL10n.tr("Vector", "large_badge_value_k_format", p1) + } + /// Later + internal static var later: String { + return VectorL10n.tr("Vector", "later") + } + /// Leave + internal static var leave: String { + return VectorL10n.tr("Vector", "leave") + } + /// Library + internal static var mediaPickerLibrary: String { + return VectorL10n.tr("Vector", "media_picker_library") + } + /// Select + internal static var mediaPickerSelect: String { + return VectorL10n.tr("Vector", "media_picker_select") + } + /// The Internet connection appears to be offline. + internal static var networkOfflinePrompt: String { + return VectorL10n.tr("Vector", "network_offline_prompt") + } + /// Next + internal static var next: String { + return VectorL10n.tr("Vector", "next") + } + /// %@ is calling you but %@ does not support calls yet.\nYou can ignore this notification and answer the call from another device or you can reject it. + internal static func noVoip(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "no_voip", p1, p2) + } + /// Incoming call + internal static var noVoipTitle: String { + return VectorL10n.tr("Vector", "no_voip_title") + } + /// Off + internal static var off: String { + return VectorL10n.tr("Vector", "off") + } + /// On + internal static var on: String { + return VectorL10n.tr("Vector", "on") + } + /// or + internal static var or: String { + return VectorL10n.tr("Vector", "or") + } + /// CONVERSATIONS + internal static var peopleConversationSection: String { + return VectorL10n.tr("Vector", "people_conversation_section") + } + /// INVITES + internal static var peopleInvitesSection: String { + return VectorL10n.tr("Vector", "people_invites_section") + } + /// No conversations + internal static var peopleNoConversation: String { + return VectorL10n.tr("Vector", "people_no_conversation") + } + /// Preview + internal static var preview: String { + return VectorL10n.tr("Vector", "preview") + } + /// Public Rooms (at %@): + internal static func publicRoomSectionTitle(_ p1: String) -> String { + return VectorL10n.tr("Vector", "public_room_section_title", p1) + } + /// You seem to be shaking the phone in frustration. Would you like to submit a bug report? + internal static var rageShakePrompt: String { + return VectorL10n.tr("Vector", "rage_shake_prompt") + } + /// Read Receipts List + internal static var readReceiptsList: String { + return VectorL10n.tr("Vector", "read_receipts_list") + } + /// Read: + internal static var receiptStatusRead: String { + return VectorL10n.tr("Vector", "receipt_status_read") + } + /// Remove + internal static var remove: String { + return VectorL10n.tr("Vector", "remove") + } + /// Rename + internal static var rename: String { + return VectorL10n.tr("Vector", "rename") + } + /// Please launch Riot on another device that can decrypt the message so it can send the keys to this device. + internal static var rerequestKeysAlertMessage: String { + return VectorL10n.tr("Vector", "rerequest_keys_alert_message") + } + /// Request Sent + internal static var rerequestKeysAlertTitle: String { + return VectorL10n.tr("Vector", "rerequest_keys_alert_title") + } + /// Retry + internal static var retry: String { + return VectorL10n.tr("Vector", "retry") + } + /// Send photo or video + internal static var roomActionSendPhotoOrVideo: String { + return VectorL10n.tr("Vector", "room_action_send_photo_or_video") + } + /// Send sticker + internal static var roomActionSendSticker: String { + return VectorL10n.tr("Vector", "room_action_send_sticker") + } + /// You need permission to manage conference call in this room + internal static var roomConferenceCallNoPower: String { + return VectorL10n.tr("Vector", "room_conference_call_no_power") + } + /// Account + internal static var roomCreationAccount: String { + return VectorL10n.tr("Vector", "room_creation_account") + } + /// Appearance + internal static var roomCreationAppearance: String { + return VectorL10n.tr("Vector", "room_creation_appearance") + } + /// Name + internal static var roomCreationAppearanceName: String { + return VectorL10n.tr("Vector", "room_creation_appearance_name") + } + /// Chat picture (optional) + internal static var roomCreationAppearancePicture: String { + return VectorL10n.tr("Vector", "room_creation_appearance_picture") + } + /// Search / invite by User ID, Name or email + internal static var roomCreationInviteAnotherUser: String { + return VectorL10n.tr("Vector", "room_creation_invite_another_user") + } + /// Keep private + internal static var roomCreationKeepPrivate: String { + return VectorL10n.tr("Vector", "room_creation_keep_private") + } + /// Make private + internal static var roomCreationMakePrivate: String { + return VectorL10n.tr("Vector", "room_creation_make_private") + } + /// Make public + internal static var roomCreationMakePublic: String { + return VectorL10n.tr("Vector", "room_creation_make_public") + } + /// Are you sure you want to make this chat public? Anyone can read your messages and join the chat. + internal static var roomCreationMakePublicPromptMsg: String { + return VectorL10n.tr("Vector", "room_creation_make_public_prompt_msg") + } + /// Make this chat public? + internal static var roomCreationMakePublicPromptTitle: String { + return VectorL10n.tr("Vector", "room_creation_make_public_prompt_title") + } + /// Privacy + internal static var roomCreationPrivacy: String { + return VectorL10n.tr("Vector", "room_creation_privacy") + } + /// This chat is private + internal static var roomCreationPrivateRoom: String { + return VectorL10n.tr("Vector", "room_creation_private_room") + } + /// This chat is public + internal static var roomCreationPublicRoom: String { + return VectorL10n.tr("Vector", "room_creation_public_room") + } + /// New Chat + internal static var roomCreationTitle: String { + return VectorL10n.tr("Vector", "room_creation_title") + } + /// A room is already being created. Please wait. + internal static var roomCreationWaitForCreation: String { + return VectorL10n.tr("Vector", "room_creation_wait_for_creation") + } + /// Delete unsent messages + internal static var roomDeleteUnsentMessages: String { + return VectorL10n.tr("Vector", "room_delete_unsent_messages") + } + /// Who can access this room? + internal static var roomDetailsAccessSection: String { + return VectorL10n.tr("Vector", "room_details_access_section") + } + /// Anyone who knows the room's link, including guests + internal static var roomDetailsAccessSectionAnyone: String { + return VectorL10n.tr("Vector", "room_details_access_section_anyone") + } + /// Anyone who knows the room's link, apart from guests + internal static var roomDetailsAccessSectionAnyoneApartFromGuest: String { + return VectorL10n.tr("Vector", "room_details_access_section_anyone_apart_from_guest") + } + /// List this room in room directory + internal static var roomDetailsAccessSectionDirectoryToggle: String { + return VectorL10n.tr("Vector", "room_details_access_section_directory_toggle") + } + /// Only people who have been invited + internal static var roomDetailsAccessSectionInvitedOnly: String { + return VectorL10n.tr("Vector", "room_details_access_section_invited_only") + } + /// To link to a room it must have an address + internal static var roomDetailsAccessSectionNoAddressWarning: String { + return VectorL10n.tr("Vector", "room_details_access_section_no_address_warning") + } + /// You will have no main address specified. The default main address for this room will be picked randomly + internal static var roomDetailsAddressesDisableMainAddressPromptMsg: String { + return VectorL10n.tr("Vector", "room_details_addresses_disable_main_address_prompt_msg") + } + /// Main address warning + internal static var roomDetailsAddressesDisableMainAddressPromptTitle: String { + return VectorL10n.tr("Vector", "room_details_addresses_disable_main_address_prompt_title") + } + /// %@ is not a valid format for an alias + internal static func roomDetailsAddressesInvalidAddressPromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_details_addresses_invalid_address_prompt_msg", p1) + } + /// Invalid alias format + internal static var roomDetailsAddressesInvalidAddressPromptTitle: String { + return VectorL10n.tr("Vector", "room_details_addresses_invalid_address_prompt_title") + } + /// Addresses + internal static var roomDetailsAddressesSection: String { + return VectorL10n.tr("Vector", "room_details_addresses_section") + } + /// Encrypt to verified devices only + internal static var roomDetailsAdvancedE2eEncryptionBlacklistUnverifiedDevices: String { + return VectorL10n.tr("Vector", "room_details_advanced_e2e_encryption_blacklist_unverified_devices") + } + /// Encryption is not enabled in this room. + internal static var roomDetailsAdvancedE2eEncryptionDisabled: String { + return VectorL10n.tr("Vector", "room_details_advanced_e2e_encryption_disabled") + } + /// Encryption is enabled in this room + internal static var roomDetailsAdvancedE2eEncryptionEnabled: String { + return VectorL10n.tr("Vector", "room_details_advanced_e2e_encryption_enabled") + } + /// End-to-end encryption is experimental and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nOnce encryption is enabled for a room it cannot be turned off again (for now).\n\nEncrypted messages will not be visible on clients that do not yet implement encryption. + internal static var roomDetailsAdvancedE2eEncryptionPromptMessage: String { + return VectorL10n.tr("Vector", "room_details_advanced_e2e_encryption_prompt_message") + } + /// Enable encryption (warning: cannot be disabled again!) + internal static var roomDetailsAdvancedEnableE2eEncryption: String { + return VectorL10n.tr("Vector", "room_details_advanced_enable_e2e_encryption") + } + /// Room ID: + internal static var roomDetailsAdvancedRoomId: String { + return VectorL10n.tr("Vector", "room_details_advanced_room_id") + } + /// Advanced + internal static var roomDetailsAdvancedSection: String { + return VectorL10n.tr("Vector", "room_details_advanced_section") + } + /// Banned users + internal static var roomDetailsBannedUsersSection: String { + return VectorL10n.tr("Vector", "room_details_banned_users_section") + } + /// Copy Room Address + internal static var roomDetailsCopyRoomAddress: String { + return VectorL10n.tr("Vector", "room_details_copy_room_address") + } + /// Copy Room ID + internal static var roomDetailsCopyRoomId: String { + return VectorL10n.tr("Vector", "room_details_copy_room_id") + } + /// Copy Room URL + internal static var roomDetailsCopyRoomUrl: String { + return VectorL10n.tr("Vector", "room_details_copy_room_url") + } + /// Direct Chat + internal static var roomDetailsDirectChat: String { + return VectorL10n.tr("Vector", "room_details_direct_chat") + } + /// Fail to add the new room addresses + internal static var roomDetailsFailToAddRoomAliases: String { + return VectorL10n.tr("Vector", "room_details_fail_to_add_room_aliases") + } + /// Fail to enable encryption in this room + internal static var roomDetailsFailToEnableEncryption: String { + return VectorL10n.tr("Vector", "room_details_fail_to_enable_encryption") + } + /// Fail to remove the room addresses + internal static var roomDetailsFailToRemoveRoomAliases: String { + return VectorL10n.tr("Vector", "room_details_fail_to_remove_room_aliases") + } + /// Fail to update the room photo + internal static var roomDetailsFailToUpdateAvatar: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_avatar") + } + /// Fail to update the history visibility + internal static var roomDetailsFailToUpdateHistoryVisibility: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_history_visibility") + } + /// Fail to update the main address + internal static var roomDetailsFailToUpdateRoomCanonicalAlias: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_canonical_alias") + } + /// Fail to update the related communities + internal static var roomDetailsFailToUpdateRoomCommunities: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_communities") + } + /// Fail to update the direct flag of this room + internal static var roomDetailsFailToUpdateRoomDirect: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_direct") + } + /// Fail to update the room directory visibility + internal static var roomDetailsFailToUpdateRoomDirectoryVisibility: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_directory_visibility") + } + /// Fail to update the room guest access + internal static var roomDetailsFailToUpdateRoomGuestAccess: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_guest_access") + } + /// Fail to update the join rule + internal static var roomDetailsFailToUpdateRoomJoinRule: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_join_rule") + } + /// Fail to update the room name + internal static var roomDetailsFailToUpdateRoomName: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_room_name") + } + /// Fail to update the topic + internal static var roomDetailsFailToUpdateTopic: String { + return VectorL10n.tr("Vector", "room_details_fail_to_update_topic") + } + /// Favourite + internal static var roomDetailsFavouriteTag: String { + return VectorL10n.tr("Vector", "room_details_favourite_tag") + } + /// Files + internal static var roomDetailsFiles: String { + return VectorL10n.tr("Vector", "room_details_files") + } + /// %@ is not a valid identifier for a community + internal static func roomDetailsFlairInvalidIdPromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_details_flair_invalid_id_prompt_msg", p1) + } + /// Invalid format + internal static var roomDetailsFlairInvalidIdPromptTitle: String { + return VectorL10n.tr("Vector", "room_details_flair_invalid_id_prompt_title") + } + /// Show flair for communities + internal static var roomDetailsFlairSection: String { + return VectorL10n.tr("Vector", "room_details_flair_section") + } + /// Who can read history? + internal static var roomDetailsHistorySection: String { + return VectorL10n.tr("Vector", "room_details_history_section") + } + /// Anyone + internal static var roomDetailsHistorySectionAnyone: String { + return VectorL10n.tr("Vector", "room_details_history_section_anyone") + } + /// Members only (since the point in time of selecting this option) + internal static var roomDetailsHistorySectionMembersOnly: String { + return VectorL10n.tr("Vector", "room_details_history_section_members_only") + } + /// Members only (since they were invited) + internal static var roomDetailsHistorySectionMembersOnlySinceInvited: String { + return VectorL10n.tr("Vector", "room_details_history_section_members_only_since_invited") + } + /// Members only (since they joined) + internal static var roomDetailsHistorySectionMembersOnlySinceJoined: String { + return VectorL10n.tr("Vector", "room_details_history_section_members_only_since_joined") + } + /// Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged. + internal static var roomDetailsHistorySectionPromptMsg: String { + return VectorL10n.tr("Vector", "room_details_history_section_prompt_msg") + } + /// Privacy warning + internal static var roomDetailsHistorySectionPromptTitle: String { + return VectorL10n.tr("Vector", "room_details_history_section_prompt_title") + } + /// Low priority + internal static var roomDetailsLowPriorityTag: String { + return VectorL10n.tr("Vector", "room_details_low_priority_tag") + } + /// Mute notifications + internal static var roomDetailsMuteNotifs: String { + return VectorL10n.tr("Vector", "room_details_mute_notifs") + } + /// Add new address + internal static var roomDetailsNewAddress: String { + return VectorL10n.tr("Vector", "room_details_new_address") + } + /// Add new address (e.g. #foo%@) + internal static func roomDetailsNewAddressPlaceholder(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_details_new_address_placeholder", p1) + } + /// Add new community ID (e.g. +foo%@) + internal static func roomDetailsNewFlairPlaceholder(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_details_new_flair_placeholder", p1) + } + /// This room has no local addresses + internal static var roomDetailsNoLocalAddresses: String { + return VectorL10n.tr("Vector", "room_details_no_local_addresses") + } + /// Members + internal static var roomDetailsPeople: String { + return VectorL10n.tr("Vector", "room_details_people") + } + /// Room Photo + internal static var roomDetailsPhoto: String { + return VectorL10n.tr("Vector", "room_details_photo") + } + /// Room Name + internal static var roomDetailsRoomName: String { + return VectorL10n.tr("Vector", "room_details_room_name") + } + /// Do you want to save changes? + internal static var roomDetailsSaveChangesPrompt: String { + return VectorL10n.tr("Vector", "room_details_save_changes_prompt") + } + /// Set as Main Address + internal static var roomDetailsSetMainAddress: String { + return VectorL10n.tr("Vector", "room_details_set_main_address") + } + /// Settings + internal static var roomDetailsSettings: String { + return VectorL10n.tr("Vector", "room_details_settings") + } + /// Room Details + internal static var roomDetailsTitle: String { + return VectorL10n.tr("Vector", "room_details_title") + } + /// Topic + internal static var roomDetailsTopic: String { + return VectorL10n.tr("Vector", "room_details_topic") + } + /// Unset as Main Address + internal static var roomDetailsUnsetMainAddress: String { + return VectorL10n.tr("Vector", "room_details_unset_main_address") + } + /// No public rooms available + internal static var roomDirectoryNoPublicRoom: String { + return VectorL10n.tr("Vector", "room_directory_no_public_room") + } + /// You do not have permission to post to this room + internal static var roomDoNotHavePermissionToPost: String { + return VectorL10n.tr("Vector", "room_do_not_have_permission_to_post") + } + /// %@ does not exist + internal static func roomDoesNotExist(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_does_not_exist", p1) + } + /// Reason for banning this user + internal static var roomEventActionBanPromptReason: String { + return VectorL10n.tr("Vector", "room_event_action_ban_prompt_reason") + } + /// Cancel Download + internal static var roomEventActionCancelDownload: String { + return VectorL10n.tr("Vector", "room_event_action_cancel_download") + } + /// Cancel Send + internal static var roomEventActionCancelSend: String { + return VectorL10n.tr("Vector", "room_event_action_cancel_send") + } + /// Copy + internal static var roomEventActionCopy: String { + return VectorL10n.tr("Vector", "room_event_action_copy") + } + /// Delete + internal static var roomEventActionDelete: String { + return VectorL10n.tr("Vector", "room_event_action_delete") + } + /// Reason for kicking this user + internal static var roomEventActionKickPromptReason: String { + return VectorL10n.tr("Vector", "room_event_action_kick_prompt_reason") + } + /// More + internal static var roomEventActionMore: String { + return VectorL10n.tr("Vector", "room_event_action_more") + } + /// Permalink + internal static var roomEventActionPermalink: String { + return VectorL10n.tr("Vector", "room_event_action_permalink") + } + /// Quote + internal static var roomEventActionQuote: String { + return VectorL10n.tr("Vector", "room_event_action_quote") + } + /// Remove + internal static var roomEventActionRedact: String { + return VectorL10n.tr("Vector", "room_event_action_redact") + } + /// Report content + internal static var roomEventActionReport: String { + return VectorL10n.tr("Vector", "room_event_action_report") + } + /// Do you want to hide all messages from this user? + internal static var roomEventActionReportPromptIgnoreUser: String { + return VectorL10n.tr("Vector", "room_event_action_report_prompt_ignore_user") + } + /// Reason for reporting this content + internal static var roomEventActionReportPromptReason: String { + return VectorL10n.tr("Vector", "room_event_action_report_prompt_reason") + } + /// Resend + internal static var roomEventActionResend: String { + return VectorL10n.tr("Vector", "room_event_action_resend") + } + /// Save + internal static var roomEventActionSave: String { + return VectorL10n.tr("Vector", "room_event_action_save") + } + /// Share + internal static var roomEventActionShare: String { + return VectorL10n.tr("Vector", "room_event_action_share") + } + /// View Decrypted Source + internal static var roomEventActionViewDecryptedSource: String { + return VectorL10n.tr("Vector", "room_event_action_view_decrypted_source") + } + /// Encryption Information + internal static var roomEventActionViewEncryption: String { + return VectorL10n.tr("Vector", "room_event_action_view_encryption") + } + /// View Source + internal static var roomEventActionViewSource: String { + return VectorL10n.tr("Vector", "room_event_action_view_source") + } + /// Failed to send + internal static var roomEventFailedToSend: String { + return VectorL10n.tr("Vector", "room_event_failed_to_send") + } + /// Jump to first unread message + internal static var roomJumpToFirstUnread: String { + return VectorL10n.tr("Vector", "room_jump_to_first_unread") + } + /// %@, %@ & others are typing… + internal static func roomManyUsersAreTyping(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_many_users_are_typing", p1, p2) + } + /// Send a message (unencrypted)… + internal static var roomMessagePlaceholder: String { + return VectorL10n.tr("Vector", "room_message_placeholder") + } + /// Send a reply (unencrypted)… + internal static var roomMessageReplyToPlaceholder: String { + return VectorL10n.tr("Vector", "room_message_reply_to_placeholder") + } + /// Send a reply… + internal static var roomMessageReplyToShortPlaceholder: String { + return VectorL10n.tr("Vector", "room_message_reply_to_short_placeholder") + } + /// Send a message… + internal static var roomMessageShortPlaceholder: String { + return VectorL10n.tr("Vector", "room_message_short_placeholder") + } + /// %d new message + internal static func roomNewMessageNotification(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "room_new_message_notification", p1) + } + /// %d new messages + internal static func roomNewMessagesNotification(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "room_new_messages_notification", p1) + } + /// Connectivity to the server has been lost. + internal static var roomOfflineNotification: String { + return VectorL10n.tr("Vector", "room_offline_notification") + } + /// %@ is typing… + internal static func roomOneUserIsTyping(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_one_user_is_typing", p1) + } + /// Ongoing conference call. Join as %@ or %@. + internal static func roomOngoingConferenceCall(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_ongoing_conference_call", p1, p2) + } + /// Close + internal static var roomOngoingConferenceCallClose: String { + return VectorL10n.tr("Vector", "room_ongoing_conference_call_close") + } + /// Ongoing conference call. Join as %@ or %@. %@ it. + internal static func roomOngoingConferenceCallWithClose(_ p1: String, _ p2: String, _ p3: String) -> String { + return VectorL10n.tr("Vector", "room_ongoing_conference_call_with_close", p1, p2, p3) + } + /// Ban from this room + internal static var roomParticipantsActionBan: String { + return VectorL10n.tr("Vector", "room_participants_action_ban") + } + /// Hide all messages from this user + internal static var roomParticipantsActionIgnore: String { + return VectorL10n.tr("Vector", "room_participants_action_ignore") + } + /// Invite + internal static var roomParticipantsActionInvite: String { + return VectorL10n.tr("Vector", "room_participants_action_invite") + } + /// Leave this room + internal static var roomParticipantsActionLeave: String { + return VectorL10n.tr("Vector", "room_participants_action_leave") + } + /// Mention + internal static var roomParticipantsActionMention: String { + return VectorL10n.tr("Vector", "room_participants_action_mention") + } + /// Remove from this room + internal static var roomParticipantsActionRemove: String { + return VectorL10n.tr("Vector", "room_participants_action_remove") + } + /// Admin tools + internal static var roomParticipantsActionSectionAdminTools: String { + return VectorL10n.tr("Vector", "room_participants_action_section_admin_tools") + } + /// Devices + internal static var roomParticipantsActionSectionDevices: String { + return VectorL10n.tr("Vector", "room_participants_action_section_devices") + } + /// Direct chats + internal static var roomParticipantsActionSectionDirectChats: String { + return VectorL10n.tr("Vector", "room_participants_action_section_direct_chats") + } + /// Other + internal static var roomParticipantsActionSectionOther: String { + return VectorL10n.tr("Vector", "room_participants_action_section_other") + } + /// Make admin + internal static var roomParticipantsActionSetAdmin: String { + return VectorL10n.tr("Vector", "room_participants_action_set_admin") + } + /// Reset to normal user + internal static var roomParticipantsActionSetDefaultPowerLevel: String { + return VectorL10n.tr("Vector", "room_participants_action_set_default_power_level") + } + /// Make moderator + internal static var roomParticipantsActionSetModerator: String { + return VectorL10n.tr("Vector", "room_participants_action_set_moderator") + } + /// Start new chat + internal static var roomParticipantsActionStartNewChat: String { + return VectorL10n.tr("Vector", "room_participants_action_start_new_chat") + } + /// Start video call + internal static var roomParticipantsActionStartVideoCall: String { + return VectorL10n.tr("Vector", "room_participants_action_start_video_call") + } + /// Start voice call + internal static var roomParticipantsActionStartVoiceCall: String { + return VectorL10n.tr("Vector", "room_participants_action_start_voice_call") + } + /// Unban + internal static var roomParticipantsActionUnban: String { + return VectorL10n.tr("Vector", "room_participants_action_unban") + } + /// Show all messages from this user + internal static var roomParticipantsActionUnignore: String { + return VectorL10n.tr("Vector", "room_participants_action_unignore") + } + /// Add participant + internal static var roomParticipantsAddParticipant: String { + return VectorL10n.tr("Vector", "room_participants_add_participant") + } + /// ago + internal static var roomParticipantsAgo: String { + return VectorL10n.tr("Vector", "room_participants_ago") + } + /// Filter room members + internal static var roomParticipantsFilterRoomMembers: String { + return VectorL10n.tr("Vector", "room_participants_filter_room_members") + } + /// Idle + internal static var roomParticipantsIdle: String { + return VectorL10n.tr("Vector", "room_participants_idle") + } + /// Search / invite by User ID, Name or email + internal static var roomParticipantsInviteAnotherUser: String { + return VectorL10n.tr("Vector", "room_participants_invite_another_user") + } + /// Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain' + internal static var roomParticipantsInviteMalformedId: String { + return VectorL10n.tr("Vector", "room_participants_invite_malformed_id") + } + /// Invite Error + internal static var roomParticipantsInviteMalformedIdTitle: String { + return VectorL10n.tr("Vector", "room_participants_invite_malformed_id_title") + } + /// Are you sure you want to invite %@ to this chat? + internal static func roomParticipantsInvitePromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_participants_invite_prompt_msg", p1) + } + /// Confirmation + internal static var roomParticipantsInvitePromptTitle: String { + return VectorL10n.tr("Vector", "room_participants_invite_prompt_title") + } + /// INVITED + internal static var roomParticipantsInvitedSection: String { + return VectorL10n.tr("Vector", "room_participants_invited_section") + } + /// Are you sure you want to leave the room? + internal static var roomParticipantsLeavePromptMsg: String { + return VectorL10n.tr("Vector", "room_participants_leave_prompt_msg") + } + /// Leave room + internal static var roomParticipantsLeavePromptTitle: String { + return VectorL10n.tr("Vector", "room_participants_leave_prompt_title") + } + /// %d participants + internal static func roomParticipantsMultiParticipants(_ p1: Int) -> String { + return VectorL10n.tr("Vector", "room_participants_multi_participants", p1) + } + /// now + internal static var roomParticipantsNow: String { + return VectorL10n.tr("Vector", "room_participants_now") + } + /// Offline + internal static var roomParticipantsOffline: String { + return VectorL10n.tr("Vector", "room_participants_offline") + } + /// 1 participant + internal static var roomParticipantsOneParticipant: String { + return VectorL10n.tr("Vector", "room_participants_one_participant") + } + /// Online + internal static var roomParticipantsOnline: String { + return VectorL10n.tr("Vector", "room_participants_online") + } + /// Are you sure you want to remove %@ from this chat? + internal static func roomParticipantsRemovePromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_participants_remove_prompt_msg", p1) + } + /// Confirmation + internal static var roomParticipantsRemovePromptTitle: String { + return VectorL10n.tr("Vector", "room_participants_remove_prompt_title") + } + /// Remove third-party invite is not supported yet until the api exists + internal static var roomParticipantsRemoveThirdPartyInviteMsg: String { + return VectorL10n.tr("Vector", "room_participants_remove_third_party_invite_msg") + } + /// Participants + internal static var roomParticipantsTitle: String { + return VectorL10n.tr("Vector", "room_participants_title") + } + /// Unknown + internal static var roomParticipantsUnknown: String { + return VectorL10n.tr("Vector", "room_participants_unknown") + } + /// This room is a continuation of another conversation. + internal static var roomPredecessorInformation: String { + return VectorL10n.tr("Vector", "room_predecessor_information") + } + /// Tap here to see older messages. + internal static var roomPredecessorLink: String { + return VectorL10n.tr("Vector", "room_predecessor_link") + } + /// You have been invited to join this room by %@ + internal static func roomPreviewInvitationFormat(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_preview_invitation_format", p1) + } + /// This is a preview of this room. Room interactions have been disabled. + internal static var roomPreviewSubtitle: String { + return VectorL10n.tr("Vector", "room_preview_subtitle") + } + /// You are trying to access %@. Would you like to join in order to participate in the discussion? + internal static func roomPreviewTryJoinAnUnknownRoom(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_preview_try_join_an_unknown_room", p1) + } + /// a room + internal static var roomPreviewTryJoinAnUnknownRoomDefault: String { + return VectorL10n.tr("Vector", "room_preview_try_join_an_unknown_room_default") + } + /// This invitation was sent to %@, which is not associated with this account. You may wish to login with a different account, or add this email to your this account. + internal static func roomPreviewUnlinkedEmailWarning(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_preview_unlinked_email_warning", p1) + } + /// cancel all + internal static var roomPromptCancel: String { + return VectorL10n.tr("Vector", "room_prompt_cancel") + } + /// Resend all + internal static var roomPromptResend: String { + return VectorL10n.tr("Vector", "room_prompt_resend") + } + /// ROOMS + internal static var roomRecentsConversationsSection: String { + return VectorL10n.tr("Vector", "room_recents_conversations_section") + } + /// Create room + internal static var roomRecentsCreateEmptyRoom: String { + return VectorL10n.tr("Vector", "room_recents_create_empty_room") + } + /// ROOM DIRECTORY + internal static var roomRecentsDirectorySection: String { + return VectorL10n.tr("Vector", "room_recents_directory_section") + } + /// Network + internal static var roomRecentsDirectorySectionNetwork: String { + return VectorL10n.tr("Vector", "room_recents_directory_section_network") + } + /// FAVOURITES + internal static var roomRecentsFavouritesSection: String { + return VectorL10n.tr("Vector", "room_recents_favourites_section") + } + /// INVITES + internal static var roomRecentsInvitesSection: String { + return VectorL10n.tr("Vector", "room_recents_invites_section") + } + /// Join room + internal static var roomRecentsJoinRoom: String { + return VectorL10n.tr("Vector", "room_recents_join_room") + } + /// Type a room id or a room alias + internal static var roomRecentsJoinRoomPrompt: String { + return VectorL10n.tr("Vector", "room_recents_join_room_prompt") + } + /// Join a room + internal static var roomRecentsJoinRoomTitle: String { + return VectorL10n.tr("Vector", "room_recents_join_room_title") + } + /// LOW PRIORITY + internal static var roomRecentsLowPrioritySection: String { + return VectorL10n.tr("Vector", "room_recents_low_priority_section") + } + /// No rooms + internal static var roomRecentsNoConversation: String { + return VectorL10n.tr("Vector", "room_recents_no_conversation") + } + /// PEOPLE + internal static var roomRecentsPeopleSection: String { + return VectorL10n.tr("Vector", "room_recents_people_section") + } + /// SYSTEM ALERTS + internal static var roomRecentsServerNoticeSection: String { + return VectorL10n.tr("Vector", "room_recents_server_notice_section") + } + /// Start chat + internal static var roomRecentsStartChatWith: String { + return VectorL10n.tr("Vector", "room_recents_start_chat_with") + } + /// This room has been replaced and is no longer active. + internal static var roomReplacementInformation: String { + return VectorL10n.tr("Vector", "room_replacement_information") + } + /// The conversation continues here. + internal static var roomReplacementLink: String { + return VectorL10n.tr("Vector", "room_replacement_link") + } + /// Resend unsent messages + internal static var roomResendUnsentMessages: String { + return VectorL10n.tr("Vector", "room_resend_unsent_messages") + } + /// Please + internal static var roomResourceLimitExceededMessageContact1: String { + return VectorL10n.tr("Vector", "room_resource_limit_exceeded_message_contact_1") + } + /// contact your service administrator + internal static var roomResourceLimitExceededMessageContact2Link: String { + return VectorL10n.tr("Vector", "room_resource_limit_exceeded_message_contact_2_link") + } + /// to continue using this service. + internal static var roomResourceLimitExceededMessageContact3: String { + return VectorL10n.tr("Vector", "room_resource_limit_exceeded_message_contact_3") + } + /// This homeserver has exceeded one of its resource limits so + internal static var roomResourceUsageLimitReachedMessage1Default: String { + return VectorL10n.tr("Vector", "room_resource_usage_limit_reached_message_1_default") + } + /// This homeserver has hit its Monthly Active User limit so + internal static var roomResourceUsageLimitReachedMessage1MonthlyActiveUser: String { + return VectorL10n.tr("Vector", "room_resource_usage_limit_reached_message_1_monthly_active_user") + } + /// some users will not be able to log in. + internal static var roomResourceUsageLimitReachedMessage2: String { + return VectorL10n.tr("Vector", "room_resource_usage_limit_reached_message_2") + } + /// to get this limit increased. + internal static var roomResourceUsageLimitReachedMessageContact3: String { + return VectorL10n.tr("Vector", "room_resource_usage_limit_reached_message_contact_3") + } + /// Invite members + internal static var roomTitleInviteMembers: String { + return VectorL10n.tr("Vector", "room_title_invite_members") + } + /// %@ members + internal static func roomTitleMembers(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_title_members", p1) + } + /// %@/%@ active members + internal static func roomTitleMultipleActiveMembers(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_title_multiple_active_members", p1, p2) + } + /// New room + internal static var roomTitleNewRoom: String { + return VectorL10n.tr("Vector", "room_title_new_room") + } + /// %@/%@ active member + internal static func roomTitleOneActiveMember(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_title_one_active_member", p1, p2) + } + /// 1 member + internal static var roomTitleOneMember: String { + return VectorL10n.tr("Vector", "room_title_one_member") + } + /// %@ & %@ are typing… + internal static func roomTwoUsersAreTyping(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_two_users_are_typing", p1, p2) + } + /// Messages not sent. %@ or %@ now? + internal static func roomUnsentMessagesNotification(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_unsent_messages_notification", p1, p2) + } + /// Message not sent due to unknown devices being present. %@ or %@ now? + internal static func roomUnsentMessagesUnknownDevicesNotification(_ p1: String, _ p2: String) -> String { + return VectorL10n.tr("Vector", "room_unsent_messages_unknown_devices_notification", p1, p2) + } + /// End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption. + internal static var roomWarningAboutEncryption: String { + return VectorL10n.tr("Vector", "room_warning_about_encryption") + } + /// Save + internal static var save: String { + return VectorL10n.tr("Vector", "save") + } + /// Search + internal static var searchDefaultPlaceholder: String { + return VectorL10n.tr("Vector", "search_default_placeholder") + } + /// Files + internal static var searchFiles: String { + return VectorL10n.tr("Vector", "search_files") + } + /// Searching… + internal static var searchInProgress: String { + return VectorL10n.tr("Vector", "search_in_progress") + } + /// Messages + internal static var searchMessages: String { + return VectorL10n.tr("Vector", "search_messages") + } + /// No results + internal static var searchNoResult: String { + return VectorL10n.tr("Vector", "search_no_result") + } + /// People + internal static var searchPeople: String { + return VectorL10n.tr("Vector", "search_people") + } + /// Search by User ID, Name or email + internal static var searchPeoplePlaceholder: String { + return VectorL10n.tr("Vector", "search_people_placeholder") + } + /// Rooms + internal static var searchRooms: String { + return VectorL10n.tr("Vector", "search_rooms") + } + /// Send to %@ + internal static func sendTo(_ p1: String) -> String { + return VectorL10n.tr("Vector", "send_to", p1) + } + /// Sending + internal static var sending: String { + return VectorL10n.tr("Vector", "sending") + } + /// Add email address + internal static var settingsAddEmailAddress: String { + return VectorL10n.tr("Vector", "settings_add_email_address") + } + /// Add phone number + internal static var settingsAddPhoneNumber: String { + return VectorL10n.tr("Vector", "settings_add_phone_number") + } + /// ADVANCED + internal static var settingsAdvanced: String { + return VectorL10n.tr("Vector", "settings_advanced") + } + /// Receive incoming calls on your lock screen. See your Riot calls in the system's call history. If iCloud is enabled, this call history will be shared with Apple. + internal static var settingsCallkitInfo: String { + return VectorL10n.tr("Vector", "settings_callkit_info") + } + /// CALLS + internal static var settingsCallsSettings: String { + return VectorL10n.tr("Vector", "settings_calls_settings") + } + /// Change password + internal static var settingsChangePassword: String { + return VectorL10n.tr("Vector", "settings_change_password") + } + /// Clear cache + internal static var settingsClearCache: String { + return VectorL10n.tr("Vector", "settings_clear_cache") + } + /// Home server is %@ + internal static func settingsConfigHomeServer(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_config_home_server", p1) + } + /// Identity server is %@ + internal static func settingsConfigIdentityServer(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_config_identity_server", p1) + } + /// No build info + internal static var settingsConfigNoBuildInfo: String { + return VectorL10n.tr("Vector", "settings_config_no_build_info") + } + /// Logged in as %@ + internal static func settingsConfigUserId(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_config_user_id", p1) + } + /// confirm password + internal static var settingsConfirmPassword: String { + return VectorL10n.tr("Vector", "settings_confirm_password") + } + /// LOCAL CONTACTS + internal static var settingsContacts: String { + return VectorL10n.tr("Vector", "settings_contacts") + } + /// Use emails and phone numbers to discover users + internal static var settingsContactsDiscoverMatrixUsers: String { + return VectorL10n.tr("Vector", "settings_contacts_discover_matrix_users") + } + /// Phonebook country + internal static var settingsContactsPhonebookCountry: String { + return VectorL10n.tr("Vector", "settings_contacts_phonebook_country") + } + /// Copyright + internal static var settingsCopyright: String { + return VectorL10n.tr("Vector", "settings_copyright") + } + /// https://riot.im/copyright + internal static var settingsCopyrightUrl: String { + return VectorL10n.tr("Vector", "settings_copyright_url") + } + /// Encrypt to verified devices only + internal static var settingsCryptoBlacklistUnverifiedDevices: String { + return VectorL10n.tr("Vector", "settings_crypto_blacklist_unverified_devices") + } + /// \nDevice ID: + internal static var settingsCryptoDeviceId: String { + return VectorL10n.tr("Vector", "settings_crypto_device_id") + } + /// \nDevice key: + internal static var settingsCryptoDeviceKey: String { + return VectorL10n.tr("Vector", "settings_crypto_device_key") + } + /// Device name: + internal static var settingsCryptoDeviceName: String { + return VectorL10n.tr("Vector", "settings_crypto_device_name") + } + /// Export keys + internal static var settingsCryptoExport: String { + return VectorL10n.tr("Vector", "settings_crypto_export") + } + /// CRYPTOGRAPHY + internal static var settingsCryptography: String { + return VectorL10n.tr("Vector", "settings_cryptography") + } + /// DEACTIVATE ACCOUNT + internal static var settingsDeactivateAccount: String { + return VectorL10n.tr("Vector", "settings_deactivate_account") + } + /// Deactivate my account + internal static var settingsDeactivateMyAccount: String { + return VectorL10n.tr("Vector", "settings_deactivate_my_account") + } + /// DEVICES + internal static var settingsDevices: String { + return VectorL10n.tr("Vector", "settings_devices") + } + /// Display Name + internal static var settingsDisplayName: String { + return VectorL10n.tr("Vector", "settings_display_name") + } + /// Email + internal static var settingsEmailAddress: String { + return VectorL10n.tr("Vector", "settings_email_address") + } + /// Enter your email address + internal static var settingsEmailAddressPlaceholder: String { + return VectorL10n.tr("Vector", "settings_email_address_placeholder") + } + /// Integrated calling + internal static var settingsEnableCallkit: String { + return VectorL10n.tr("Vector", "settings_enable_callkit") + } + /// Notifications on this device + internal static var settingsEnablePushNotif: String { + return VectorL10n.tr("Vector", "settings_enable_push_notif") + } + /// Rage shake to report bug + internal static var settingsEnableRageshake: String { + return VectorL10n.tr("Vector", "settings_enable_rageshake") + } + /// Fail to update password + internal static var settingsFailToUpdatePassword: String { + return VectorL10n.tr("Vector", "settings_fail_to_update_password") + } + /// Fail to update profile + internal static var settingsFailToUpdateProfile: String { + return VectorL10n.tr("Vector", "settings_fail_to_update_profile") + } + /// First Name + internal static var settingsFirstName: String { + return VectorL10n.tr("Vector", "settings_first_name") + } + /// Show flair where allowed + internal static var settingsFlair: String { + return VectorL10n.tr("Vector", "settings_flair") + } + /// Global notification settings are available on your %@ web client + internal static func settingsGlobalSettingsInfo(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_global_settings_info", p1) + } + /// IGNORED USERS + internal static var settingsIgnoredUsers: String { + return VectorL10n.tr("Vector", "settings_ignored_users") + } + /// KEY BACKUP + internal static var settingsKeyBackup: String { + return VectorL10n.tr("Vector", "settings_key_backup") + } + /// Start using Key Backup + internal static var settingsKeyBackupButtonCreate: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_create") + } + /// Delete Backup + internal static var settingsKeyBackupButtonDelete: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_delete") + } + /// Restore from Backup + internal static var settingsKeyBackupButtonRestore: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_restore") + } + /// Use key backup + internal static var settingsKeyBackupButtonUse: String { + return VectorL10n.tr("Vector", "settings_key_backup_button_use") + } + /// Are you sure? You will lose your encrypted messages if your keys are not backed up properly. + internal static var settingsKeyBackupDeleteConfirmationPromptMsg: String { + return VectorL10n.tr("Vector", "settings_key_backup_delete_confirmation_prompt_msg") + } + /// Delete Backup + internal static var settingsKeyBackupDeleteConfirmationPromptTitle: String { + return VectorL10n.tr("Vector", "settings_key_backup_delete_confirmation_prompt_title") + } + /// Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages. + internal static var settingsKeyBackupInfo: String { + return VectorL10n.tr("Vector", "settings_key_backup_info") + } + /// Algorithm: %@ + internal static func settingsKeyBackupInfoAlgorithm(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_algorithm", p1) + } + /// Checking... + internal static var settingsKeyBackupInfoChecking: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_checking") + } + /// Your keys are not being backed up from this device. + internal static var settingsKeyBackupInfoNone: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_none") + } + /// This device is not backing up your keys. + internal static var settingsKeyBackupInfoNotValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_not_valid") + } + /// Backing up %@ keys... + internal static func settingsKeyBackupInfoProgress(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_progress", p1) + } + /// All keys backed up + internal static var settingsKeyBackupInfoProgressDone: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done") + } + /// Back up your keys before signing out to avoid losing them. + internal static var settingsKeyBackupInfoSignoutWarning: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_signout_warning") + } + /// Backup has an invalid signature from %@ + internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_unverified", p1) + } + /// Backup has an invalid signature from %@ + internal static func settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_invalid_device_verified", p1) + } + /// Backup has a signature from device with ID: %@ + internal static func settingsKeyBackupInfoTrustSignatureUnknown(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_unknown", p1) + } + /// Backup has a valid signature from this device + internal static var settingsKeyBackupInfoTrustSignatureValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid") + } + /// Backup has a signature from %@ + internal static func settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_unverified", p1) + } + /// Backup has a valid signature from %@ + internal static func settingsKeyBackupInfoTrustSignatureValidDeviceVerified(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_trust_signature_valid_device_verified", p1) + } + /// This device is backing up your keys. + internal static var settingsKeyBackupInfoValid: String { + return VectorL10n.tr("Vector", "settings_key_backup_info_valid") + } + /// Key Backup Version: %@ + internal static func settingsKeyBackupInfoVersion(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_key_backup_info_version", p1) + } + /// LABS + internal static var settingsLabs: String { + return VectorL10n.tr("Vector", "settings_labs") + } + /// Create conference calls with jitsi + internal static var settingsLabsCreateConferenceWithJitsi: String { + return VectorL10n.tr("Vector", "settings_labs_create_conference_with_jitsi") + } + /// End-to-End Encryption + internal static var settingsLabsE2eEncryption: String { + return VectorL10n.tr("Vector", "settings_labs_e2e_encryption") + } + /// To finish setting up encryption you must log in again. + internal static var settingsLabsE2eEncryptionPromptMessage: String { + return VectorL10n.tr("Vector", "settings_labs_e2e_encryption_prompt_message") + } + /// Lazy load rooms members + internal static var settingsLabsRoomMembersLazyLoading: String { + return VectorL10n.tr("Vector", "settings_labs_room_members_lazy_loading") + } + /// Your homeserver does not support lazy loading of room members yet. Try later. + internal static var settingsLabsRoomMembersLazyLoadingErrorMessage: String { + return VectorL10n.tr("Vector", "settings_labs_room_members_lazy_loading_error_message") + } + /// Mark all messages as read + internal static var settingsMarkAllAsRead: String { + return VectorL10n.tr("Vector", "settings_mark_all_as_read") + } + /// new password + internal static var settingsNewPassword: String { + return VectorL10n.tr("Vector", "settings_new_password") + } + /// Night Mode + internal static var settingsNightMode: String { + return VectorL10n.tr("Vector", "settings_night_mode") + } + /// NOTIFICATION SETTINGS + internal static var settingsNotificationsSettings: String { + return VectorL10n.tr("Vector", "settings_notifications_settings") + } + /// old password + internal static var settingsOldPassword: String { + return VectorL10n.tr("Vector", "settings_old_password") + } + /// Olm Version %@ + internal static func settingsOlmVersion(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_olm_version", p1) + } + /// Notifications are denied for %@, please allow them in your device settings + internal static func settingsOnDeniedNotification(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_on_denied_notification", p1) + } + /// OTHER + internal static var settingsOther: String { + return VectorL10n.tr("Vector", "settings_other") + } + /// Your password has been updated + internal static var settingsPasswordUpdated: String { + return VectorL10n.tr("Vector", "settings_password_updated") + } + /// Phone + internal static var settingsPhoneNumber: String { + return VectorL10n.tr("Vector", "settings_phone_number") + } + /// Pin rooms with missed notifications + internal static var settingsPinRoomsWithMissedNotif: String { + return VectorL10n.tr("Vector", "settings_pin_rooms_with_missed_notif") + } + /// Pin rooms with unread messages + internal static var settingsPinRoomsWithUnread: String { + return VectorL10n.tr("Vector", "settings_pin_rooms_with_unread") + } + /// Privacy Policy + internal static var settingsPrivacyPolicy: String { + return VectorL10n.tr("Vector", "settings_privacy_policy") + } + /// https://riot.im/privacy + internal static var settingsPrivacyPolicyUrl: String { + return VectorL10n.tr("Vector", "settings_privacy_policy_url") + } + /// Profile Picture + internal static var settingsProfilePicture: String { + return VectorL10n.tr("Vector", "settings_profile_picture") + } + /// Are you sure you want to remove the email address %@? + internal static func settingsRemoveEmailPromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_remove_email_prompt_msg", p1) + } + /// Are you sure you want to remove the phone number %@? + internal static func settingsRemovePhonePromptMsg(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_remove_phone_prompt_msg", p1) + } + /// Confirmation + internal static var settingsRemovePromptTitle: String { + return VectorL10n.tr("Vector", "settings_remove_prompt_title") + } + /// Report bug + internal static var settingsReportBug: String { + return VectorL10n.tr("Vector", "settings_report_bug") + } + /// Send anon crash & usage data + internal static var settingsSendCrashReport: String { + return VectorL10n.tr("Vector", "settings_send_crash_report") + } + /// Show decrypted content + internal static var settingsShowDecryptedContent: String { + return VectorL10n.tr("Vector", "settings_show_decrypted_content") + } + /// Sign Out + internal static var settingsSignOut: String { + return VectorL10n.tr("Vector", "settings_sign_out") + } + /// Are you sure? + internal static var settingsSignOutConfirmation: String { + return VectorL10n.tr("Vector", "settings_sign_out_confirmation") + } + /// You will lose your end-to-end encryption keys. That means you will no longer be able to read old messages in encrypted rooms on this device. + internal static var settingsSignOutE2eWarn: String { + return VectorL10n.tr("Vector", "settings_sign_out_e2e_warn") + } + /// Surname + internal static var settingsSurname: String { + return VectorL10n.tr("Vector", "settings_surname") + } + /// Terms & Conditions + internal static var settingsTermConditions: String { + return VectorL10n.tr("Vector", "settings_term_conditions") + } + /// https://riot.im/tac_apple + internal static var settingsTermConditionsUrl: String { + return VectorL10n.tr("Vector", "settings_term_conditions_url") + } + /// Third-party Notices + internal static var settingsThirdPartyNotices: String { + return VectorL10n.tr("Vector", "settings_third_party_notices") + } + /// Settings + internal static var settingsTitle: String { + return VectorL10n.tr("Vector", "settings_title") + } + /// Language + internal static var settingsUiLanguage: String { + return VectorL10n.tr("Vector", "settings_ui_language") + } + /// Theme + internal static var settingsUiTheme: String { + return VectorL10n.tr("Vector", "settings_ui_theme") + } + /// Auto + internal static var settingsUiThemeAuto: String { + return VectorL10n.tr("Vector", "settings_ui_theme_auto") + } + /// Black + internal static var settingsUiThemeBlack: String { + return VectorL10n.tr("Vector", "settings_ui_theme_black") + } + /// Dark + internal static var settingsUiThemeDark: String { + return VectorL10n.tr("Vector", "settings_ui_theme_dark") + } + /// Light + internal static var settingsUiThemeLight: String { + return VectorL10n.tr("Vector", "settings_ui_theme_light") + } + /// "Auto" uses your device "Invert Colours" settings + internal static var settingsUiThemePickerMessage: String { + return VectorL10n.tr("Vector", "settings_ui_theme_picker_message") + } + /// Select a theme + internal static var settingsUiThemePickerTitle: String { + return VectorL10n.tr("Vector", "settings_ui_theme_picker_title") + } + /// Show all messages from %@? + internal static func settingsUnignoreUser(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_unignore_user", p1) + } + /// USER INTERFACE + internal static var settingsUserInterface: String { + return VectorL10n.tr("Vector", "settings_user_interface") + } + /// USER SETTINGS + internal static var settingsUserSettings: String { + return VectorL10n.tr("Vector", "settings_user_settings") + } + /// Version %@ + internal static func settingsVersion(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_version", p1) + } + /// Login in the main app to share content + internal static var shareExtensionAuthPrompt: String { + return VectorL10n.tr("Vector", "share_extension_auth_prompt") + } + /// Failed to send. Check in the main app the encryption settings for this room + internal static var shareExtensionFailedToEncrypt: String { + return VectorL10n.tr("Vector", "share_extension_failed_to_encrypt") + } + /// Sign out + internal static var signOutExistingKeyBackupAlertSignOutAction: String { + return VectorL10n.tr("Vector", "sign_out_existing_key_backup_alert_sign_out_action") + } + /// Are you sure you want to sign out? + internal static var signOutExistingKeyBackupAlertTitle: String { + return VectorL10n.tr("Vector", "sign_out_existing_key_backup_alert_title") + } + /// I'll wait + internal static var signOutKeyBackupInProgressAlertCancelAction: String { + return VectorL10n.tr("Vector", "sign_out_key_backup_in_progress_alert_cancel_action") + } + /// I don't want my encrypted messages + internal static var signOutKeyBackupInProgressAlertDiscardKeyBackupAction: String { + return VectorL10n.tr("Vector", "sign_out_key_backup_in_progress_alert_discard_key_backup_action") + } + /// Key backup in progress. If you sign out now you’ll lose access to your encrypted messages. + internal static var signOutKeyBackupInProgressAlertTitle: String { + return VectorL10n.tr("Vector", "sign_out_key_backup_in_progress_alert_title") + } + /// I don't want my encrypted messages + internal static var signOutNonExistingKeyBackupAlertDiscardKeyBackupAction: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_alert_discard_key_backup_action") + } + /// Start using Key Backup + internal static var signOutNonExistingKeyBackupAlertSetupKeyBackupAction: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_alert_setup_key_backup_action") + } + /// You’ll lose access to your encrypted messages if you sign out now + internal static var signOutNonExistingKeyBackupAlertTitle: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_alert_title") + } + /// Backup + internal static var signOutNonExistingKeyBackupSignOutConfirmationAlertBackupAction: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action") + } + /// You'll lose access to your encrypted messages unless you back up your keys before signing out. + internal static var signOutNonExistingKeyBackupSignOutConfirmationAlertMessage: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_sign_out_confirmation_alert_message") + } + /// Sign out + internal static var signOutNonExistingKeyBackupSignOutConfirmationAlertSignOutAction: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action") + } + /// You'll lose your encrypted messages + internal static var signOutNonExistingKeyBackupSignOutConfirmationAlertTitle: String { + return VectorL10n.tr("Vector", "sign_out_non_existing_key_backup_sign_out_confirmation_alert_title") + } + /// Start + internal static var start: String { + return VectorL10n.tr("Vector", "start") + } + /// Favourites + internal static var titleFavourites: String { + return VectorL10n.tr("Vector", "title_favourites") + } + /// Communities + internal static var titleGroups: String { + return VectorL10n.tr("Vector", "title_groups") + } + /// Home + internal static var titleHome: String { + return VectorL10n.tr("Vector", "title_home") + } + /// People + internal static var titlePeople: String { + return VectorL10n.tr("Vector", "title_people") + } + /// Rooms + internal static var titleRooms: String { + return VectorL10n.tr("Vector", "title_rooms") + } + /// Today + internal static var today: String { + return VectorL10n.tr("Vector", "today") + } + /// This room contains unknown devices which have not been verified.\nThis means there is no guarantee that the devices belong to the users they claim to.\nWe recommend you go through the verification process for each device before continuing, but you can resend the message without verifying if you prefer. + internal static var unknownDevicesAlert: String { + return VectorL10n.tr("Vector", "unknown_devices_alert") + } + /// Room contains unknown devices + internal static var unknownDevicesAlertTitle: String { + return VectorL10n.tr("Vector", "unknown_devices_alert_title") + } + /// Answer Anyway + internal static var unknownDevicesAnswerAnyway: String { + return VectorL10n.tr("Vector", "unknown_devices_answer_anyway") + } + /// Call Anyway + internal static var unknownDevicesCallAnyway: String { + return VectorL10n.tr("Vector", "unknown_devices_call_anyway") + } + /// Send Anyway + internal static var unknownDevicesSendAnyway: String { + return VectorL10n.tr("Vector", "unknown_devices_send_anyway") + } + /// Unknown devices + internal static var unknownDevicesTitle: String { + return VectorL10n.tr("Vector", "unknown_devices_title") + } + /// Verify… + internal static var unknownDevicesVerify: String { + return VectorL10n.tr("Vector", "unknown_devices_verify") + } + /// Video + internal static var video: String { + return VectorL10n.tr("Vector", "video") + } + /// View + internal static var view: String { + return VectorL10n.tr("Vector", "view") + } + /// Voice + internal static var voice: String { + return VectorL10n.tr("Vector", "voice") + } + /// Warning + internal static var warning: String { + return VectorL10n.tr("Vector", "warning") + } + /// Widget creation has failed + internal static var widgetCreationFailure: String { + return VectorL10n.tr("Vector", "widget_creation_failure") + } + /// Failed to send request. + internal static var widgetIntegrationFailedToSendRequest: String { + return VectorL10n.tr("Vector", "widget_integration_failed_to_send_request") + } + /// Missing room_id in request. + internal static var widgetIntegrationMissingRoomId: String { + return VectorL10n.tr("Vector", "widget_integration_missing_room_id") + } + /// Missing user_id in request. + internal static var widgetIntegrationMissingUserId: String { + return VectorL10n.tr("Vector", "widget_integration_missing_user_id") + } + /// You are not in this room. + internal static var widgetIntegrationMustBeInRoom: String { + return VectorL10n.tr("Vector", "widget_integration_must_be_in_room") + } + /// You need to be able to invite users to do that. + internal static var widgetIntegrationNeedToBeAbleToInvite: String { + return VectorL10n.tr("Vector", "widget_integration_need_to_be_able_to_invite") + } + /// You do not have permission to do that in this room. + internal static var widgetIntegrationNoPermissionInRoom: String { + return VectorL10n.tr("Vector", "widget_integration_no_permission_in_room") + } + /// Power level must be positive integer. + internal static var widgetIntegrationPositivePowerLevel: String { + return VectorL10n.tr("Vector", "widget_integration_positive_power_level") + } + /// This room is not recognised. + internal static var widgetIntegrationRoomNotRecognised: String { + return VectorL10n.tr("Vector", "widget_integration_room_not_recognised") + } + /// Room %@ is not visible. + internal static func widgetIntegrationRoomNotVisible(_ p1: String) -> String { + return VectorL10n.tr("Vector", "widget_integration_room_not_visible", p1) + } + /// Unable to create widget. + internal static var widgetIntegrationUnableToCreate: String { + return VectorL10n.tr("Vector", "widget_integration_unable_to_create") + } + /// You need permission to manage widgets in this room + internal static var widgetNoPowerToManage: String { + return VectorL10n.tr("Vector", "widget_no_power_to_manage") + } + /// You don't currently have any stickerpacks enabled. + internal static var widgetStickerPickerNoStickerpacksAlert: String { + return VectorL10n.tr("Vector", "widget_sticker_picker_no_stickerpacks_alert") + } + /// Add some now? + internal static var widgetStickerPickerNoStickerpacksAlertAddNow: String { + return VectorL10n.tr("Vector", "widget_sticker_picker_no_stickerpacks_alert_add_now") + } + /// Yesterday + internal static var yesterday: String { + return VectorL10n.tr("Vector", "yesterday") + } + /// You + internal static var you: String { + return VectorL10n.tr("Vector", "you") + } +} +// swiftlint:enable function_parameter_count identifier_name line_length type_body_length + +// MARK: - Implementation Details + +extension VectorL10n { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + let format = NSLocalizedString(key, tableName: table, bundle: Bundle(for: BundleToken.self), comment: "") + let locale: Locale + if let localeIdentifier = Bundle.mxk_language() { + locale = Locale(identifier: localeIdentifier) + } else if let fallbackLocaleIdentifier = Bundle.mxk_fallbackLanguage() { + locale = Locale(identifier: fallbackLocaleIdentifier) + } else { + locale = Locale.current + } + + return String(format: format, locale: locale, arguments: args) + } +} + +private final class BundleToken {} diff --git a/Riot/Managers/Analytics/Analytics.m b/Riot/Managers/Analytics/Analytics.m index a87b29315..552e9e0ab 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -103,7 +103,7 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; - (void)trackScreen:(NSString *)screenName { // Use the same pattern as Android - NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; NSString *appVersion = [AppDelegate theDelegate].appVersion; [[PiwikTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName] diff --git a/Riot/Managers/PasswordStrength/PasswordStrength.swift b/Riot/Managers/PasswordStrength/PasswordStrength.swift new file mode 100644 index 000000000..a9081dcba --- /dev/null +++ b/Riot/Managers/PasswordStrength/PasswordStrength.swift @@ -0,0 +1,27 @@ +/* + 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 + +/// Paswword strength +enum PasswordStrength: UInt { + + case tooGuessable + case veryGuessable + case somewhatGuessable + case safelyUnguessable + case veryUnguessable +} diff --git a/Riot/Managers/PasswordStrength/PasswordStrengthManager.swift b/Riot/Managers/PasswordStrength/PasswordStrengthManager.swift new file mode 100644 index 000000000..8e48d6c0d --- /dev/null +++ b/Riot/Managers/PasswordStrength/PasswordStrengthManager.swift @@ -0,0 +1,47 @@ +/* + 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 zxcvbn_ios + +/// PasswordStrengthManager computes password strength for a given string. +final class PasswordStrengthManager { + + // MARK: - Properties + + private let zxcvbn = DBZxcvbn() + + // MARK: - Public + + func passwordStrength(for password: String) -> PasswordStrength { + guard let result = zxcvbn.passwordStrength(password) else { + return .tooGuessable + } + return self.passwordStrength(from: result.score) + } + + // MARK: - Private + + private func passwordStrength(from zxcvbnScore: Int32) -> PasswordStrength { + let passwordStrengthRawValue = UInt(zxcvbnScore) + + guard let passwordStrength = PasswordStrength(rawValue: passwordStrengthRawValue) else { + return .tooGuessable + } + + return passwordStrength + } +} diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index e91b0ab15..6db8ec7b3 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -31,7 +31,14 @@ final class RiotSettings: NSObject { static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif" static let pinRoomsWithUnreadMessages = "pinRoomsWithUnread" } - + + /// Riot Standard Room Member Power Level + @objc + public enum RoomPowerLevel: Int { + case moderator = 50 + case admin = 100 + } + static let shared = RiotSettings() // MARK: - Public diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift new file mode 100644 index 000000000..839f4641c --- /dev/null +++ b/Riot/Managers/Theme/Theme.swift @@ -0,0 +1,103 @@ +/* + Copyright 2018 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 + +/// Provide color constant values defined by the designer +/// https://app.zeplin.io/project/5c122fa790c5b4241ffa6be7/screen/5c619592daff2f1241d82e75 +@objc protocol Theme { + + var backgroundColor: UIColor { get } + var baseColor: UIColor { get } + + var baseTextPrimaryColor: UIColor { get } + var baseTextSecondaryColor: UIColor { get } + + var searchBackgroundColor: UIColor { get } + var searchPlaceholderColor: UIColor { get } + + var headerBackgroundColor: UIColor { get } + var headerBorderColor: UIColor { get } + var headerTextPrimaryColor: UIColor { get } + var headerTextSecondaryColor: UIColor { get } + + var textPrimaryColor: UIColor { get } + var textSecondaryColor: UIColor { get } + + var tintColor: UIColor { get } + + var unreadRoomIndentColor: UIColor { get } + + var lineBreakColor: UIColor { get } + + var noticeColor: UIColor { get } + var noticeSecondaryColor: UIColor { get } + + /// Color for errors or warnings + var warningColor: UIColor { get } + + var avatarColors: [UIColor] { get } + + + // MARK: - Appearance and style + + + /// Status bar style to use + var statusBarStyle: UIStatusBarStyle { get } + + var scrollBarStyle: UIScrollViewIndicatorStyle { get } + + var keyboardAppearance : UIKeyboardAppearance { get } + + + // MARK: - Colors not defined in the design palette + + + /// nil is used to keep the default color + var placeholderTextColor: UIColor { get } + + /// nil is used to keep the default color + var selectedBackgroundColor: UIColor? { get } + + /// fading behind dialog modals + var overlayBackgroundColor: UIColor { get } + + /// Color to tint the search background image + var matrixSearchBackgroundImageTintColor: UIColor { get } + + // MARK: - Customisation methods + + + /// Apply the theme on a navigation bar + /// + /// - Parameter navigationBar: the navigation bar to customise. + func applyStyle(onNavigationBar: UINavigationBar) + + /// Apply the theme on a search bar. + /// + /// - Parameter searchBar: the search bar to customise. + func applyStyle(onSearchBar: UISearchBar) + + /// Apply the theme on a text field. + /// + /// - Parameter textField: the text field to customise. + func applyStyle(onTextField textField: UITextField) + + /// Apply the theme on a button. + /// + /// - Parameter button: The button to customise. + func applyStyle(onButton button: UIButton) +} diff --git a/Riot/Managers/Theme/ThemeService.h b/Riot/Managers/Theme/ThemeService.h new file mode 100644 index 000000000..4097896f5 --- /dev/null +++ b/Riot/Managers/Theme/ThemeService.h @@ -0,0 +1,71 @@ +/* + Copyright 2015 OpenMarket Ltd + Copyright 2017 Vector Creations Ltd + 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 + +@protocol Theme; + +NS_ASSUME_NONNULL_BEGIN + +/** + Posted when the user interface theme has been changed. + */ +extern NSString *const kThemeServiceDidChangeThemeNotification; + + +/** + `ThemeService` class manages the application design values. + */ +@interface ThemeService : NSObject + +/** + Returns the shared instance. + + @return the shared instance. + */ ++ (instancetype)shared; + +/** + The id of the theme being used. + */ +@property (nonatomic, nullable) NSString *themeId; + +/** + The current theme. + Default value is the Default theme. + */ +@property (nonatomic, readonly) id theme; + +/** + Get the theme with the given id. + + @param themeId the theme id. + @return the theme. + */ +- (id)themeWithThemeId:(NSString*)themeId; + +#pragma mark - Riot Colors not yet themeable + +@property (nonatomic, readonly) UIColor *riotColorBlue; +@property (nonatomic, readonly) UIColor *riotColorCuriousBlue; +@property (nonatomic, readonly) UIColor *riotColorIndigo; +@property (nonatomic, readonly) UIColor *riotColorOrange; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Riot/Managers/Theme/ThemeService.m b/Riot/Managers/Theme/ThemeService.m new file mode 100644 index 000000000..2c54ec5ad --- /dev/null +++ b/Riot/Managers/Theme/ThemeService.m @@ -0,0 +1,116 @@ +/* + Copyright 2016 OpenMarket Ltd + Copyright 2017 Vector Creations Ltd + 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 "ThemeService.h" + +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif + +NSString *const kThemeServiceDidChangeThemeNotification = @"kThemeServiceDidChangeThemeNotification"; + +@implementation ThemeService +@synthesize themeId = _themeId; + ++ (ThemeService *)shared +{ + static ThemeService *sharedOnceInstance; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedOnceInstance = [ThemeService new]; + }); + + return sharedOnceInstance; +} + +- (void)setThemeId:(NSString *)theThemeId +{ + // Update the current theme + _themeId = theThemeId; + self.theme = [self themeWithThemeId:self.themeId]; +} + +- (void)setTheme:(id _Nonnull)theme +{ + _theme = theme; + + [UIScrollView appearance].indicatorStyle = self.theme.scrollBarStyle; + + [[NSNotificationCenter defaultCenter] postNotificationName:kThemeServiceDidChangeThemeNotification object:nil]; +} + +- (id)themeWithThemeId:(NSString*)themeId +{ + id theme; + + if ([themeId isEqualToString:@"auto"]) + { + // Translate "auto" into a theme + themeId = UIAccessibilityIsInvertColorsEnabled() ? @"dark" : @"light"; + } + + if ([themeId isEqualToString:@"dark"]) + { + theme = [DarkTheme new]; + } + else if ([themeId isEqualToString:@"black"]) + { + theme = [BlackTheme new]; + } + else + { + // Use light theme by default + theme = [DefaultTheme new]; + } + + return theme; +} + +#pragma mark - Private methods + +- (instancetype)init +{ + self = [super init]; + if (self) + { + // Riot Colors not yet themeable + _riotColorBlue = [[UIColor alloc] initWithRgb:0x81BDDB]; + _riotColorCuriousBlue = [[UIColor alloc] initWithRgb:0x2A9EDB]; + _riotColorIndigo = [[UIColor alloc] initWithRgb:0xBD79CC]; + _riotColorOrange = [[UIColor alloc] initWithRgb:0xF8A15F]; + + // Observe "Invert Colours" settings changes (available since iOS 11) + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accessibilityInvertColorsStatusDidChange) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil]; + } + return self; +} + +- (void)accessibilityInvertColorsStatusDidChange +{ + // Refresh the theme only for "auto" + if ([self.themeId isEqualToString:@"auto"]) + { + self.theme = [self themeWithThemeId:self.themeId]; + } +} + + +@end diff --git a/Riot/Managers/Theme/Themes/BlackTheme.swift b/Riot/Managers/Theme/Themes/BlackTheme.swift new file mode 100644 index 000000000..74ea1d52e --- /dev/null +++ b/Riot/Managers/Theme/Themes/BlackTheme.swift @@ -0,0 +1,28 @@ +/* + 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 + +class BlackTheme: DarkTheme { + + override init() { + super.init() + self.backgroundColor = UIColor(rgb: 0x000000) + self.baseColor = UIColor(rgb: 0x060708) + self.headerBackgroundColor = UIColor(rgb: 0x090A0C) + self.headerBorderColor = UIColor(rgb: 0x0D0F12) + } +} diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift new file mode 100644 index 000000000..89d3cc92b --- /dev/null +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -0,0 +1,92 @@ +/* + Copyright 2018 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 + +/// Color constants for the dark theme +@objcMembers +class DarkTheme: NSObject, Theme { + + var backgroundColor: UIColor = UIColor(rgb: 0x181B21) + + var baseColor: UIColor = UIColor(rgb: 0x1B1F25) + var baseTextPrimaryColor: UIColor = UIColor(rgb: 0xEDF3FF) + var baseTextSecondaryColor: UIColor = UIColor(rgb: 0xEDF3FF) + + var searchBackgroundColor: UIColor = UIColor(rgb: 0x181B21) + var searchPlaceholderColor: UIColor = UIColor(rgb: 0x61708B) + + var headerBackgroundColor: UIColor = UIColor(rgb: 0x22262E) + var headerBorderColor: UIColor = UIColor(rgb: 0x181B21) + var headerTextPrimaryColor: UIColor = UIColor(rgb: 0xA1B2D1) + var headerTextSecondaryColor: UIColor = UIColor(rgb: 0xC8C8CD) + + var textPrimaryColor: UIColor = UIColor(rgb: 0xEDF3FF) + var textSecondaryColor: UIColor = UIColor(rgb: 0xA1B2D1) + + var tintColor: UIColor = UIColor(rgb: 0x03B381) + var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) + var lineBreakColor: UIColor = UIColor(rgb: 0x61708B) + + var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) + var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) + + var warningColor: UIColor = UIColor(rgb: 0xFF4B55) + + var avatarColors: [UIColor] = [ + UIColor(rgb: 0x03B381), + UIColor(rgb: 0x368BD6), + UIColor(rgb: 0xAC3BA8)] + + var statusBarStyle: UIStatusBarStyle = .lightContent + var scrollBarStyle: UIScrollViewIndicatorStyle = .white + var keyboardAppearance: UIKeyboardAppearance = .dark + + var placeholderTextColor: UIColor = UIColor(white: 1.0, alpha: 0.3) + var selectedBackgroundColor: UIColor? = UIColor.black + var overlayBackgroundColor: UIColor = UIColor(white: 0.7, alpha: 0.5) + var matrixSearchBackgroundImageTintColor: UIColor = UIColor(rgb: 0x7E7E7E) + + func applyStyle(onNavigationBar navigationBar: UINavigationBar) { + navigationBar.tintColor = self.baseTextPrimaryColor; + navigationBar.titleTextAttributes = [ + NSAttributedStringKey.foregroundColor: self.baseTextPrimaryColor + ] + navigationBar.barTintColor = self.baseColor; + + // The navigation bar needs to be opaque so that its background color is the expected one + navigationBar.isTranslucent = false; + } + + func applyStyle(onSearchBar searchBar: UISearchBar) { + searchBar.barStyle = .black + searchBar.tintColor = self.searchPlaceholderColor; + searchBar.barTintColor = self.headerBackgroundColor; + searchBar.layer.borderWidth = 1; + searchBar.layer.borderColor = self.headerBorderColor.cgColor; + } + + func applyStyle(onTextField texField: UITextField) { + texField.textColor = self.textPrimaryColor + texField.tintColor = self.tintColor + } + + func applyStyle(onButton button: UIButton) { + // NOTE: Tint color does nothing by default on button type `UIButtonType.custom` + button.tintColor = self.tintColor + } +} diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift new file mode 100644 index 000000000..3a14b3f95 --- /dev/null +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -0,0 +1,92 @@ +/* + Copyright 2018 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 + +/// Color constants for the default theme +@objcMembers +class DefaultTheme: NSObject, Theme { + + var backgroundColor: UIColor = UIColor(rgb: 0xFFFFFF) + + var baseColor: UIColor = UIColor(rgb: 0x27303A) + var baseTextPrimaryColor: UIColor = UIColor(rgb: 0xFFFFFF) + var baseTextSecondaryColor: UIColor = UIColor(rgb: 0xFFFFFF) + + var searchBackgroundColor: UIColor = UIColor(rgb: 0xFFFFFF) + var searchPlaceholderColor: UIColor = UIColor(rgb: 0x61708B) + + var headerBackgroundColor: UIColor = UIColor(rgb: 0xF2F5F8) + var headerBorderColor: UIColor = UIColor(rgb: 0xE9EDF1) + var headerTextPrimaryColor: UIColor = UIColor(rgb: 0x61708B) + var headerTextSecondaryColor: UIColor = UIColor(rgb: 0xC8C8CD) + + var textPrimaryColor: UIColor = UIColor(rgb: 0x2E2F32) + var textSecondaryColor: UIColor = UIColor(rgb: 0x9E9E9E) + + var tintColor: UIColor = UIColor(rgb: 0x03B381) + var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) + var lineBreakColor: UIColor = UIColor(rgb: 0xEEEFEF) + + var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) + var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) + + var warningColor: UIColor = UIColor(rgb: 0xFF4B55) + + var avatarColors: [UIColor] = [ + UIColor(rgb: 0x03B381), + UIColor(rgb: 0x368BD6), + UIColor(rgb: 0xAC3BA8)] + + var statusBarStyle: UIStatusBarStyle = .lightContent + var scrollBarStyle: UIScrollViewIndicatorStyle = .default + var keyboardAppearance: UIKeyboardAppearance = .light + + var placeholderTextColor: UIColor = UIColor(white: 0.7, alpha: 1.0) // Use default 70% gray color + var selectedBackgroundColor: UIColor? = nil // Use the default selection color + var overlayBackgroundColor: UIColor = UIColor(white: 0.7, alpha: 0.5) + var matrixSearchBackgroundImageTintColor: UIColor = UIColor(rgb: 0xE7E7E7) + + func applyStyle(onNavigationBar navigationBar: UINavigationBar) { + navigationBar.tintColor = self.baseTextPrimaryColor; + navigationBar.titleTextAttributes = [ + NSAttributedStringKey.foregroundColor: self.baseTextPrimaryColor + ] + navigationBar.barTintColor = self.baseColor; + + // The navigation bar needs to be opaque so that its background color is the expected one + navigationBar.isTranslucent = false; + } + + func applyStyle(onSearchBar searchBar: UISearchBar) { + searchBar.barStyle = .default + searchBar.tintColor = self.searchPlaceholderColor; + searchBar.barTintColor = self.headerBackgroundColor; + searchBar.layer.borderWidth = 1; + searchBar.layer.borderColor = self.headerBorderColor.cgColor; + } + + func applyStyle(onTextField texField: UITextField) { + texField.textColor = self.textPrimaryColor + texField.tintColor = self.tintColor + } + + func applyStyle(onButton button: UIButton) { + // NOTE: Tint color does nothing by default on button type `UIButtonType.custom` + button.tintColor = self.tintColor + } +} diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index cd16fe9d5..4efb30d30 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -18,6 +18,7 @@ #import "AuthenticationViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "AuthInputsView.h" #import "ForgotPasswordInputsView.h" @@ -36,9 +37,9 @@ NSString *defaultCountryCode; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -120,7 +121,7 @@ self.authInputsView = authInputsView; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -130,53 +131,55 @@ - (void)userInterfaceThemeDidChange { - self.view.backgroundColor = kRiotSecondaryBgColor; - - self.navigationBar.barTintColor = kRiotSecondaryBgColor; - self.authenticationScrollView.backgroundColor = kRiotPrimaryBgColor; - self.authFallbackContentView.backgroundColor = kRiotPrimaryBgColor; - - if (kRiotPlaceholderTextColor) + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationBar]; + + // This view controller is not part of a navigation controller + // so that applyStyleOnNavigationBar does not fully work. + // In order to have the right status bar color, use the expected status bar color + // as the main view background color. + // Hopefully, subviews define their own background color with `theme.backgroundColor`, + // which makes all work together. + self.view.backgroundColor = ThemeService.shared.theme.baseColor; + + self.authenticationScrollView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.authFallbackContentView.backgroundColor = ThemeService.shared.theme.backgroundColor; + + if (self.homeServerTextField.placeholder) { - if (self.homeServerTextField.placeholder) - { - self.homeServerTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.homeServerTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - if (self.identityServerTextField.placeholder) - { - self.identityServerTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.identityServerTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + self.homeServerTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.homeServerTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + if (self.identityServerTextField.placeholder) + { + self.identityServerTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.identityServerTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; } - self.submitButton.backgroundColor = kRiotColorGreen; - self.skipButton.backgroundColor = kRiotColorGreen; + self.submitButton.backgroundColor = ThemeService.shared.theme.tintColor; + self.skipButton.backgroundColor = ThemeService.shared.theme.tintColor; - self.noFlowLabel.textColor = kRiotColorRed; + self.noFlowLabel.textColor = ThemeService.shared.theme.warningColor; NSMutableAttributedString *forgotPasswordTitle = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_forgot_password", @"Vector", nil)]; - [forgotPasswordTitle addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0, forgotPasswordTitle.length)]; - [forgotPasswordTitle addAttribute:NSForegroundColorAttributeName value:kRiotColorGreen range:NSMakeRange(0, forgotPasswordTitle.length)]; + [forgotPasswordTitle addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, forgotPasswordTitle.length)]; + [forgotPasswordTitle addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.tintColor range:NSMakeRange(0, forgotPasswordTitle.length)]; [self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateNormal]; [self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateHighlighted]; [self updateForgotPwdButtonVisibility]; - NSAttributedString *serverOptionsTitle = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_use_server_options", @"Vector", nil) attributes:@{NSForegroundColorAttributeName : kRiotSecondaryTextColor, NSFontAttributeName: [UIFont systemFontOfSize:14]}]; + NSAttributedString *serverOptionsTitle = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_use_server_options", @"Vector", nil) attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor, NSFontAttributeName: [UIFont systemFontOfSize:14]}]; [self.customServersTickButton setAttributedTitle:serverOptionsTitle forState:UIControlStateNormal]; [self.customServersTickButton setAttributedTitle:serverOptionsTitle forState:UIControlStateHighlighted]; - self.homeServerTextField.textColor = kRiotPrimaryTextColor; - self.homeServerLabel.textColor = kRiotSecondaryTextColor; + self.homeServerTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.homeServerLabel.textColor = ThemeService.shared.theme.textSecondaryColor; - self.identityServerTextField.textColor = kRiotPrimaryTextColor; - self.identityServerLabel.textColor = kRiotSecondaryTextColor; - - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + self.identityServerTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.identityServerLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; [self.authInputsView customizeViewRendering]; @@ -185,7 +188,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -218,10 +221,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -290,7 +293,7 @@ if (!countryCode) { // If none, consider the preferred locale - NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]; + NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[NSBundle mainBundle] preferredLocalizations][0]]; if ([local respondsToSelector:@selector(countryCode)]) { countryCode = local.countryCode; diff --git a/Riot/Modules/Authentication/Views/AuthInputsView.m b/Riot/Modules/Authentication/Views/AuthInputsView.m index 9aa81ff8f..c8b11a933 100644 --- a/Riot/Modules/Authentication/Views/AuthInputsView.m +++ b/Riot/Modules/Authentication/Views/AuthInputsView.m @@ -17,7 +17,7 @@ #import "AuthInputsView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "Tools.h" #import "CountryPickerViewController.h" @@ -71,12 +71,9 @@ self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_id_placeholder", @"Vector", nil); self.repeatPasswordTextField.placeholder = NSLocalizedStringFromTable(@"auth_repeat_password_placeholder", @"Vector", nil); self.passWordTextField.placeholder = NSLocalizedStringFromTable(@"auth_password_placeholder", @"Vector", nil); - - if (kRiotPlaceholderTextColor) - { - // Apply placeholder color - [self customizeViewRendering]; - } + + // Apply placeholder color + [self customizeViewRendering]; } - (void)destroy @@ -103,56 +100,53 @@ { [super customizeViewRendering]; - self.repeatPasswordTextField.textColor = kRiotPrimaryTextColor; - self.userLoginTextField.textColor = kRiotPrimaryTextColor; - self.passWordTextField.textColor = kRiotPrimaryTextColor; + self.repeatPasswordTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.userLoginTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.passWordTextField.textColor = ThemeService.shared.theme.textPrimaryColor; - self.emailTextField.textColor = kRiotPrimaryTextColor; - self.phoneTextField.textColor = kRiotPrimaryTextColor; + self.emailTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.phoneTextField.textColor = ThemeService.shared.theme.textPrimaryColor; - self.isoCountryCodeLabel.textColor = kRiotPrimaryTextColor; - self.callingCodeLabel.textColor = kRiotPrimaryTextColor; + self.isoCountryCodeLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.callingCodeLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.messageLabel.textColor = kRiotSecondaryTextColor; + self.messageLabel.textColor = ThemeService.shared.theme.textSecondaryColor; self.messageLabel.numberOfLines = 0; - - if (kRiotPlaceholderTextColor) + + if (self.userLoginTextField.placeholder) { - if (self.userLoginTextField.placeholder) - { - self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.userLoginTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - - if (self.repeatPasswordTextField.placeholder) - { - self.repeatPasswordTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.repeatPasswordTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - - } - - if (self.passWordTextField.placeholder) - { - self.passWordTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.passWordTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - - if (self.phoneTextField.placeholder) - { - self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.phoneTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - - if (self.emailTextField.placeholder) - { - self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.emailTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.userLoginTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + + if (self.repeatPasswordTextField.placeholder) + { + self.repeatPasswordTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.repeatPasswordTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + + } + + if (self.passWordTextField.placeholder) + { + self.passWordTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.passWordTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + + if (self.phoneTextField.placeholder) + { + self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.phoneTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + + if (self.emailTextField.placeholder) + { + self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.emailTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; } } @@ -195,16 +189,13 @@ self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_id_placeholder", @"Vector", nil); self.messageLabel.text = NSLocalizedStringFromTable(@"or", @"Vector", nil); self.phoneTextField.placeholder = NSLocalizedStringFromTable(@"auth_phone_placeholder", @"Vector", nil); - - if (kRiotPlaceholderTextColor) - { - self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.userLoginTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.phoneTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + + self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.userLoginTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.phoneTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; self.userLoginContainer.hidden = NO; self.messageLabel.hidden = NO; @@ -1024,17 +1015,10 @@ if (thirdPartyIdentifiersHidden) { self.passWordTextField.returnKeyType = UIReturnKeyNext; - - if (kRiotPlaceholderTextColor) - { - self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:NSLocalizedStringFromTable(@"auth_user_name_placeholder", @"Vector", nil) - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - else - { - self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_name_placeholder", @"Vector", nil); - } + + self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:NSLocalizedStringFromTable(@"auth_user_name_placeholder", @"Vector", nil) + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; self.userLoginContainer.hidden = NO; self.passwordContainer.hidden = NO; @@ -1057,12 +1041,9 @@ self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_optional_email_placeholder", @"Vector", nil); } - if (kRiotPlaceholderTextColor) - { - self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] + self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.emailTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; self.emailContainer.hidden = NO; @@ -1085,12 +1066,9 @@ self.phoneTextField.placeholder = NSLocalizedStringFromTable(@"auth_optional_phone_placeholder", @"Vector", nil); } - if (kRiotPlaceholderTextColor) - { - self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.phoneTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.phoneTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; self.phoneContainer.hidden = NO; @@ -1149,18 +1127,7 @@ phoneNumberPickerNavigationController = [[RiotNavigationController alloc] init]; // Set Riot navigation bar colors - phoneNumberPickerNavigationController.navigationBar.barTintColor = kRiotPrimaryBgColor; - NSDictionary *titleTextAttributes = phoneNumberPickerNavigationController.navigationBar.titleTextAttributes; - if (titleTextAttributes) - { - NSMutableDictionary *textAttributes = [NSMutableDictionary dictionaryWithDictionary:titleTextAttributes]; - textAttributes[NSForegroundColorAttributeName] = kRiotPrimaryTextColor; - phoneNumberPickerNavigationController.navigationBar.titleTextAttributes = textAttributes; - } - else if (kRiotPrimaryTextColor) - { - phoneNumberPickerNavigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor}; - } + [ThemeService.shared.theme applyStyleOnNavigationBar:phoneNumberPickerNavigationController.navigationBar]; [phoneNumberPickerNavigationController pushViewController:phoneNumberCountryPicker animated:NO]; @@ -1292,11 +1259,11 @@ // Retrieve the site key NSString *siteKey; - id recaptchaParams = [currentSession.params objectForKey:kMXLoginFlowTypeRecaptcha]; + id recaptchaParams = currentSession.params[kMXLoginFlowTypeRecaptcha]; if (recaptchaParams && [recaptchaParams isKindOfClass:NSDictionary.class]) { NSDictionary *recaptchaParamsDict = (NSDictionary*)recaptchaParams; - siteKey = [recaptchaParamsDict objectForKey:@"public_key"]; + siteKey = recaptchaParamsDict[@"public_key"]; } // Retrieve the REST client from delegate @@ -1331,6 +1298,9 @@ reCaptchaWebView.translatesAutoresizingMaskIntoConstraints = NO; [self.recaptchaContainer addSubview:reCaptchaWebView]; + // Disable the webview scrollView to avoid 2 scrollviews on the same screen + reCaptchaWebView.scrollView.scrollEnabled = NO; + [self.recaptchaContainer addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"|-[view]-|" options:0 diff --git a/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m b/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m index 653598d3a..6f16f2533 100644 --- a/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m +++ b/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m @@ -18,7 +18,8 @@ #import "ForgotPasswordInputsView.h" #import "MXHTTPOperation.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @interface ForgotPasswordInputsView () @@ -59,11 +60,8 @@ self.passWordTextField.placeholder = NSLocalizedStringFromTable(@"auth_new_password_placeholder", @"Vector", nil); self.repeatPasswordTextField.placeholder = NSLocalizedStringFromTable(@"auth_repeat_new_password_placeholder", @"Vector", nil); - if (kRiotPlaceholderTextColor) - { - // Apply placeholder color - [self customizeViewRendering]; - } + // Apply placeholder color + [self customizeViewRendering]; } - (void)destroy @@ -104,38 +102,35 @@ { [super customizeViewRendering]; - self.messageLabel.textColor = kRiotPrimaryTextColor; + self.messageLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.emailTextField.textColor = kRiotPrimaryTextColor; - self.passWordTextField.textColor = kRiotPrimaryTextColor; - self.repeatPasswordTextField.textColor = kRiotPrimaryTextColor; + self.emailTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.passWordTextField.textColor = ThemeService.shared.theme.textPrimaryColor; + self.repeatPasswordTextField.textColor = ThemeService.shared.theme.textPrimaryColor; self.messageLabel.numberOfLines = 0; [self.nextStepButton.layer setCornerRadius:5]; self.nextStepButton.clipsToBounds = YES; - self.nextStepButton.backgroundColor = kRiotColorGreen; - - if (kRiotPlaceholderTextColor) + self.nextStepButton.backgroundColor = ThemeService.shared.theme.tintColor; + + if (self.emailTextField.placeholder) { - if (self.emailTextField.placeholder) - { - self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.emailTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - if (self.passWordTextField.placeholder) - { - self.passWordTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.passWordTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } - if (self.repeatPasswordTextField.placeholder) - { - self.repeatPasswordTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:self.repeatPasswordTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.emailTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + if (self.passWordTextField.placeholder) + { + self.passWordTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.passWordTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; + } + if (self.repeatPasswordTextField.placeholder) + { + self.repeatPasswordTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:self.repeatPasswordTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; } } diff --git a/Riot/Modules/Authentication/Views/TermsView.swift b/Riot/Modules/Authentication/Views/TermsView.swift index 443b9486e..7a387fd7c 100644 --- a/Riot/Modules/Authentication/Views/TermsView.swift +++ b/Riot/Modules/Authentication/Views/TermsView.swift @@ -80,7 +80,9 @@ final class TermsView: UIView, NibOwnerLoadable, UITableViewDelegate, UITableVie } func customizeViewRendering() { - acceptButton.backgroundColor = kRiotColorGreen + self.backgroundColor = UIColor.clear + self.tableView.backgroundColor = UIColor.clear + acceptButton.backgroundColor = ThemeService.shared().theme.tintColor } @@ -137,17 +139,16 @@ final class TermsView: UIView, NibOwnerLoadable, UITableViewDelegate, UITableVie cell.label.text = policy.name cell.isEnabled = accepted cell.accessoryType = .disclosureIndicator + cell.backgroundColor = UIColor.clear + if let checkBox = cell.checkBox, checkBox.gestureRecognizers?.isEmpty ?? true { + let gesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapCheckbox)) + gesture.numberOfTapsRequired = 1 + gesture.numberOfTouchesRequired = 1 - let gesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapCheckbox)) - gesture.numberOfTapsRequired = 1 - gesture.numberOfTouchesRequired = 1 - - cell.checkBox.tag = indexPath.row - - cell.checkBox?.isUserInteractionEnabled = true - if (cell.checkBox?.gestureRecognizers?.count == 0) { - cell.checkBox?.addGestureRecognizer(gesture) + checkBox.isUserInteractionEnabled = true + checkBox.tag = indexPath.row + checkBox.addGestureRecognizer(gesture) } return cell @@ -188,9 +189,6 @@ final class TermsView: UIView, NibOwnerLoadable, UITableViewDelegate, UITableVie let leftBarButtonItem: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "back_icon"), style: .plain, target: self, action:#selector(didTapCancelOnPolicyScreen)) webViewViewController.navigationItem.leftBarButtonItem = leftBarButtonItem - let rightBarButtonItem: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("accept", tableName: "Vector", comment: ""), style: .plain, target: self, action: #selector(didAcceptPolicy)) - webViewViewController.navigationItem.rightBarButtonItem = rightBarButtonItem - navigationController = RiotNavigationController() delegate?.authInputsView?(nil, present: navigationController, animated: false) navigationController?.pushViewController(webViewViewController, animated: false) @@ -200,16 +198,6 @@ final class TermsView: UIView, NibOwnerLoadable, UITableViewDelegate, UITableVie removePolicyScreen() } - @objc private func didAcceptPolicy() { - - if let displayedPolicyIndex = self.displayedPolicyIndex { - acceptedPolicies.insert(displayedPolicyIndex) - } - - removePolicyScreen() - reload() - } - private func removePolicyScreen() { displayedPolicyIndex = nil diff --git a/Riot/Modules/BugReport/BugReportViewController.m b/Riot/Modules/BugReport/BugReportViewController.m index 689d2cb73..cbf17f7ec 100644 --- a/Riot/Modules/BugReport/BugReportViewController.m +++ b/Riot/Modules/BugReport/BugReportViewController.m @@ -17,6 +17,7 @@ #import "BugReportViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "GBDeviceInfo_iOS.h" @@ -27,8 +28,8 @@ // The temporary file used to store the screenshot NSURL *screenShotFile; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @property (nonatomic) BOOL sendLogs; @@ -127,7 +128,7 @@ _bugReportDescriptionTextView.inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -137,44 +138,44 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.overlayView.backgroundColor = kRiotOverlayColor; + self.overlayView.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; self.overlayView.alpha = 1.0; - self.containerView.backgroundColor = kRiotPrimaryBgColor; - self.sendingContainer.backgroundColor = kRiotPrimaryBgColor; + self.containerView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.sendingContainer.backgroundColor = ThemeService.shared.theme.backgroundColor; - self.bugReportDescriptionTextView.keyboardAppearance = kRiotKeyboard; + self.bugReportDescriptionTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance; - self.titleLabel.textColor = kRiotPrimaryTextColor; - self.sendingLabel.textColor = kRiotPrimaryTextColor; - self.descriptionLabel.textColor = kRiotPrimaryTextColor; - self.bugReportDescriptionTextView.textColor = kRiotPrimaryTextColor; - self.bugReportDescriptionTextView.tintColor = kRiotColorGreen; - self.logsDescriptionLabel.textColor = kRiotPrimaryTextColor; - self.sendLogsLabel.textColor = kRiotPrimaryTextColor; - self.sendScreenshotLabel.textColor = kRiotPrimaryTextColor; + self.titleLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.sendingLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.descriptionLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.bugReportDescriptionTextView.textColor = ThemeService.shared.theme.textPrimaryColor; + self.bugReportDescriptionTextView.tintColor = ThemeService.shared.theme.tintColor; + self.logsDescriptionLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.sendLogsLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.sendScreenshotLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.sendButton.tintColor = kRiotColorGreen; - self.cancelButton.tintColor = kRiotColorGreen; + self.sendButton.tintColor = ThemeService.shared.theme.tintColor; + self.cancelButton.tintColor = ThemeService.shared.theme.tintColor; - _bugReportDescriptionTextView.layer.borderColor = kRiotSecondaryBgColor.CGColor; + _bugReportDescriptionTextView.layer.borderColor = ThemeService.shared.theme.headerBackgroundColor.CGColor; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [super destroy]; diff --git a/Riot/Modules/Call/CallViewController.m b/Riot/Modules/Call/CallViewController.m index b88a3e32b..7753347a8 100644 --- a/Riot/Modules/Call/CallViewController.m +++ b/Riot/Modules/Call/CallViewController.m @@ -19,6 +19,7 @@ #import "CallViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "AvatarGenerator.h" @@ -36,8 +37,8 @@ // Current alert (if any). UIAlertController *currentAlert; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -87,7 +88,7 @@ [self updateLocalPreviewLayout]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -97,20 +98,20 @@ - (void)userInterfaceThemeDidChange { - self.view.backgroundColor = kRiotPrimaryBgColor; - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.barTitleColor = ThemeService.shared.theme.textPrimaryColor; + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.callerNameLabel.textColor = kRiotPrimaryTextColor; - self.callStatusLabel.textColor = kRiotTopicTextColor; + self.callerNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.callStatusLabel.textColor = ThemeService.shared.theme.baseTextSecondaryColor; - self.localPreviewContainerView.layer.borderColor = kRiotColorGreen.CGColor; + self.localPreviewContainerView.layer.borderColor = ThemeService.shared.theme.tintColor.CGColor; self.localPreviewContainerView.layer.borderWidth = 2; self.localPreviewContainerView.layer.cornerRadius = 5; self.localPreviewContainerView.clipsToBounds = YES; - self.remotePreviewContainerView.backgroundColor = kRiotPrimaryBgColor; + self.remotePreviewContainerView.backgroundColor = ThemeService.shared.theme.backgroundColor; if (gradientMaskLayer) { @@ -120,14 +121,14 @@ // Add a gradient mask programatically at the top of the screen (background of the call information (name, status)) gradientMaskLayer = [CAGradientLayer layer]; - // Consider the grayscale components of the kRiotPrimaryBgColor. + // Consider the grayscale components of the ThemeService.shared.theme.backgroundColor. CGFloat white = 1.0; - [kRiotPrimaryBgColor getWhite:&white alpha:nil]; + [ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil]; CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor; CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor; - gradientMaskLayer.colors = [NSArray arrayWithObjects:(__bridge id)opaqueWhiteColor, (__bridge id)transparentWhiteColor, nil]; + gradientMaskLayer.colors = @[(__bridge id) opaqueWhiteColor, (__bridge id) transparentWhiteColor]; gradientMaskLayer.bounds = CGRectMake(0, 0, self.callContainerView.frame.size.width, self.callContainerView.frame.size.height + 20); gradientMaskLayer.anchorPoint = CGPointZero; @@ -188,10 +189,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [gradientMaskLayer removeFromSuperlayer]; @@ -282,19 +283,9 @@ UINavigationController *usersDevicesNavigationController = [[RiotNavigationController alloc] init]; // Set Riot navigation bar colors - usersDevicesNavigationController.navigationBar.barTintColor = kRiotPrimaryBgColor; - NSDictionary *titleTextAttributes = usersDevicesNavigationController.navigationBar.titleTextAttributes; - if (titleTextAttributes) - { - NSMutableDictionary *textAttributes = [NSMutableDictionary dictionaryWithDictionary:titleTextAttributes]; - textAttributes[NSForegroundColorAttributeName] = kRiotPrimaryTextColor; - usersDevicesNavigationController.navigationBar.titleTextAttributes = textAttributes; - } - else if (kRiotPrimaryTextColor) - { - usersDevicesNavigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor}; - } - + [ThemeService.shared.theme applyStyleOnNavigationBar:usersDevicesNavigationController.navigationBar]; + usersDevicesNavigationController.navigationBar.barTintColor = ThemeService.shared.theme.backgroundColor; + [usersDevicesNavigationController pushViewController:usersDevicesViewController animated:NO]; [self presentViewController:usersDevicesNavigationController animated:YES completion:nil]; @@ -358,7 +349,8 @@ return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId withDisplayName:self.mxCall.room.summary.displayname size:self.callerImageViewWidthConstraint.constant andFontSize:fontSize]; } - return [UIImage imageNamed:@"placeholder"]; + return [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor]; } - (void)setMxCall:(MXCall *)call diff --git a/Riot/Modules/Call/Views/IncomingCallView.m b/Riot/Modules/Call/Views/IncomingCallView.m index d956cecce..bc2757b60 100644 --- a/Riot/Modules/Call/Views/IncomingCallView.m +++ b/Riot/Modules/Call/Views/IncomingCallView.m @@ -21,7 +21,8 @@ #import #import "CircleButton.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" static const CGFloat kAvatarSize = 100.0; static const CGFloat kButtonSize = 80.0; @@ -56,11 +57,11 @@ static const CGFloat kButtonSize = 80.0; self = [super initWithFrame:CGRectZero]; if (self) { - self.backgroundColor = kRiotPrimaryBgColor; + self.backgroundColor = ThemeService.shared.theme.backgroundColor; self.opaque = YES; self.callerImageView = [[MXKImageView alloc] init]; - self.callerImageView.backgroundColor = kRiotPrimaryBgColor; + self.callerImageView.backgroundColor = ThemeService.shared.theme.backgroundColor; self.callerImageView.clipsToBounds = YES; self.callerImageView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; self.callerImageView.enableInMemoryCache = YES; @@ -73,41 +74,41 @@ static const CGFloat kButtonSize = 80.0; mediaManager:mediaManager]; self.callerNameLabel = [[UILabel alloc] init]; - self.callerNameLabel.backgroundColor = kRiotPrimaryBgColor; - self.callerNameLabel.textColor = kRiotPrimaryTextColor; + self.callerNameLabel.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.callerNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; self.callerNameLabel.font = [UIFont systemFontOfSize:24.0 weight:UIFontWeightMedium]; self.callerNameLabel.text = callerName; self.callerNameLabel.textAlignment = NSTextAlignmentCenter; self.callInfoLabel = [[UILabel alloc] init]; - self.callInfoLabel.backgroundColor = kRiotPrimaryBgColor; - self.callInfoLabel.textColor = kRiotSecondaryTextColor; + self.callInfoLabel.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.callInfoLabel.textColor = ThemeService.shared.theme.textSecondaryColor; self.callInfoLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.callInfoLabel.text = callInfo; self.callInfoLabel.textAlignment = NSTextAlignmentCenter; - UIColor *answerButtonBorderColor = kRiotColorGreen; + UIColor *answerButtonBorderColor = ThemeService.shared.theme.tintColor; self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_icon"] borderColor:answerButtonBorderColor]; - self.answerButton.defaultBackgroundColor = kRiotPrimaryBgColor; + self.answerButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor; [self.answerButton addTarget:self action:@selector(didTapAnswerButton) forControlEvents:UIControlEventTouchUpInside]; self.answerTitleLabel = [[UILabel alloc] init]; - self.answerTitleLabel.backgroundColor = kRiotPrimaryBgColor; + self.answerTitleLabel.backgroundColor = ThemeService.shared.theme.backgroundColor; self.answerTitleLabel.textColor = answerButtonBorderColor; self.answerTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.answerTitleLabel.text = NSLocalizedStringFromTable(@"accept", @"Vector", nil); - UIColor *rejectButtonBorderColor = kRiotColorPinkRed; + UIColor *rejectButtonBorderColor = ThemeService.shared.theme.warningColor; self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"call_hangup_icon"] borderColor:rejectButtonBorderColor]; - self.rejectButton.defaultBackgroundColor = kRiotPrimaryBgColor; + self.rejectButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor; [self.rejectButton addTarget:self action:@selector(didTapRejectButton) forControlEvents:UIControlEventTouchUpInside]; self.rejectTitleLabel = [[UILabel alloc] init]; - self.rejectTitleLabel.backgroundColor = kRiotPrimaryBgColor; + self.rejectTitleLabel.backgroundColor = ThemeService.shared.theme.backgroundColor; self.rejectTitleLabel.textColor = rejectButtonBorderColor; self.rejectTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.rejectTitleLabel.text = NSLocalizedStringFromTable(@"decline", @"Vector", nil); diff --git a/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenter.swift b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenter.swift new file mode 100755 index 000000000..901d52bc6 --- /dev/null +++ b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenter.swift @@ -0,0 +1,119 @@ +/* + 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 + +/// Used to present activity indicator on a view +final class ActivityIndicatorPresenter: ActivityIndicatorPresenterType { + + // MARK: - Constants + + private enum Constants { + static let animationDuration: TimeInterval = 0.3 + static let backgroundOverlayColor = UIColor.clear + static let backgroundOverlayAlpha: CGFloat = 1.0 + } + + // MARK: - Properties + + private weak var backgroundOverlayView: UIView? + private weak var activityIndicatorView: ActivityIndicatorView? + private weak var presentingView: UIView? + + // MARK: - Public + + func presentActivityIndicator(on view: UIView, animated: Bool, completion: (() -> Void)? = nil) { + self.presentingView = view + + view.isUserInteractionEnabled = false + + let backgroundOverlayView = self.createBackgroundOverlayView(with: view.frame) + + let activityIndicatorView = ActivityIndicatorView() + + // Add activityIndicatorView on backgroundOverlayView centered + backgroundOverlayView.addSubview(activityIndicatorView) + activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false + activityIndicatorView.centerXAnchor.constraint(equalTo: backgroundOverlayView.centerXAnchor).isActive = true + activityIndicatorView.centerYAnchor.constraint(equalTo: backgroundOverlayView.centerYAnchor).isActive = true + + activityIndicatorView.startAnimating() + + backgroundOverlayView.alpha = 0 + backgroundOverlayView.isHidden = false + + view.vc_addSubViewMatchingParent(backgroundOverlayView) + + self.backgroundOverlayView = backgroundOverlayView + self.activityIndicatorView = activityIndicatorView + + let animationInstructions = { + backgroundOverlayView.alpha = Constants.backgroundOverlayAlpha + } + + if animated { + UIView.animate(withDuration: Constants.animationDuration, animations: { + animationInstructions() + }, completion: { _ in + completion?() + }) + } else { + animationInstructions() + completion?() + } + } + + func removeCurrentActivityIndicator(animated: Bool, completion: (() -> Void)? = nil) { + guard let presentingView = self.presentingView, + let backgroundOverlayView = self.backgroundOverlayView, + let activityIndicatorView = self.activityIndicatorView else { + return + } + + presentingView.isUserInteractionEnabled = true + + let animationInstructions = { + activityIndicatorView.alpha = 0 + } + + let animationCompletionInstructions = { + activityIndicatorView.stopAnimating() + backgroundOverlayView.isHidden = true + backgroundOverlayView.removeFromSuperview() + } + + if animated { + UIView.animate(withDuration: Constants.animationDuration, animations: { + animationInstructions() + }, completion: { _ in + animationCompletionInstructions() + }) + } else { + animationInstructions() + animationCompletionInstructions() + } + } + + // MARK: - Private + + private func createBackgroundOverlayView(with frame: CGRect = CGRect.zero) -> UIView { + let backgroundOverlayView = UIView(frame: frame) + backgroundOverlayView.backgroundColor = Constants.backgroundOverlayColor + backgroundOverlayView.alpha = Constants.backgroundOverlayAlpha + return backgroundOverlayView + } +} diff --git a/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenterType.swift b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenterType.swift new file mode 100644 index 000000000..5be34f2b9 --- /dev/null +++ b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorPresenterType.swift @@ -0,0 +1,34 @@ +/* + 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 used to present activity indicator on a view +protocol ActivityIndicatorPresenterType { + func presentActivityIndicator(on view: UIView, animated: Bool, completion: (() -> Void)?) + func removeCurrentActivityIndicator(animated: Bool, completion: (() -> Void)?) +} + +// `ActivityIndicatorPresenterType` default implementation +extension ActivityIndicatorPresenterType { + func presentActivityIndicator(on view: UIView, animated: Bool) { + self.presentActivityIndicator(on: view, animated: animated, completion: nil) + } + + func removeCurrentActivityIndicator(animated: Bool) { + self.removeCurrentActivityIndicator(animated: animated, completion: nil) + } +} diff --git a/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.swift b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.swift new file mode 100755 index 000000000..d7328611e --- /dev/null +++ b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.swift @@ -0,0 +1,91 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import Reusable + +final class ActivityIndicatorView: UIView, NibOwnerLoadable { + + // MARK: - Constants + + private enum Constants { + static let cornerRadius: CGFloat = 5.0 + static let activityIndicatorMargin = CGSize(width: 30.0, height: 30.0) + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var activityIndicatorView: UIActivityIndicatorView! + @IBOutlet private weak var activityIndicatorBackgroundView: UIView! + + // MARK: Public + + var color: UIColor? { + get { + return activityIndicatorView.color + } + set { + activityIndicatorView.color = newValue + } + } + + // MARK: - Setup + + private func commonInit() { + self.activityIndicatorBackgroundView.layer.masksToBounds = true + } + + convenience init() { + self.init(frame: CGRect.zero) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + self.commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + self.commonInit() + } + + // MARK: - Overrides + + override var intrinsicContentSize: CGSize { + return CGSize(width: self.activityIndicatorView.intrinsicContentSize.width + Constants.activityIndicatorMargin.width, + height: self.activityIndicatorView.intrinsicContentSize.height + Constants.activityIndicatorMargin.height) + } + + override func layoutSubviews() { + super.layoutSubviews() + + self.activityIndicatorBackgroundView.layer.cornerRadius = Constants.cornerRadius + } + + // MARK: - Public + + func startAnimating() { + self.activityIndicatorView.startAnimating() + } + + func stopAnimating() { + self.activityIndicatorView.stopAnimating() + } +} diff --git a/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.xib b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.xib new file mode 100755 index 000000000..874a4e665 --- /dev/null +++ b/Riot/Modules/Common/ActivityIndicator/ActivityIndicatorView.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Common/KeyboardAvoiding/KeyboardAvoider.swift b/Riot/Modules/Common/KeyboardAvoiding/KeyboardAvoider.swift new file mode 100644 index 000000000..aa23f7efc --- /dev/null +++ b/Riot/Modules/Common/KeyboardAvoiding/KeyboardAvoider.swift @@ -0,0 +1,130 @@ +/* + 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 + +/// Avoid keyboard overlap with scroll view content +final class KeyboardAvoider { + + // MARK: - Constants + + private enum KeyboardAnimation { + static let defaultDuration: TimeInterval = 0.25 + static let defaultAnimationCurveRawValue: Int = UIViewAnimationCurve.easeInOut.rawValue + } + + // MARK: - Properties + + weak var scrollViewContainerView: UIView? + weak var scrollView: UIScrollView? + + // MARK: - Setup + + /// Designated initializer. + /// + /// - Parameter scrollViewContainerView: The view that wraps the scroll view. + /// - Parameter scrollView: The scroll view containing keyboard inputs and where content view overlap with keyboard should be avoided. + init(scrollViewContainerView: UIView, scrollView: UIScrollView) { + self.scrollViewContainerView = scrollViewContainerView + self.scrollView = scrollView + } + + // MARK: - Public + + /// Start keyboard avoiding + func startAvoiding() { + self.registerKeyboardNotifications() + } + + /// Stop keyboard avoiding + func stopAvoiding() { + self.unregisterKeyboardNotifications() + } + + // MARK: - Private + + private func registerKeyboardNotifications() { + let notificationCenter = NotificationCenter.default + notificationCenter.addObserver( + self, + selector: #selector(keyboardWillShow(notification:)), + name: .UIKeyboardWillShow, + object: nil) + notificationCenter.addObserver( + self, + selector: #selector(keyboardWillHide(notification:)), + name: .UIKeyboardWillHide, + object: nil) + } + + private func unregisterKeyboardNotifications() { + NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil) + } + + @objc private func keyboardWillShow(notification: Notification) { + guard let view = self.scrollViewContainerView, let scrollView = self.scrollView else { + return + } + + guard let keyboardNotification = KeyboardNotification(notification: notification), + let keyboardFrame = keyboardNotification.keyboardFrameEnd else { + return + } + + let animationDuration = keyboardNotification.animationDuration ?? KeyboardAnimation.defaultDuration + + let animationOptions = keyboardNotification.animationOptions(fallbackAnimationCurveValue: KeyboardAnimation.defaultAnimationCurveRawValue) + + // Transform the keyboard's frame into our view's coordinate system + let keyboardFrameInView = view.convert(keyboardFrame, from: nil) + + // Find how much the keyboard overlaps the scroll view + let scrollViewBottomInset = scrollView.frame.maxY - keyboardFrameInView.origin.y + + UIView.animate(withDuration: animationDuration, + delay: 0.0, + options: animationOptions, animations: { + + scrollView.contentInset.bottom = scrollViewBottomInset + scrollView.scrollIndicatorInsets.bottom = scrollViewBottomInset + }, completion: nil) + } + + @objc private func keyboardWillHide(notification: Notification) { + guard let scrollView = self.scrollView else { + return + } + + guard let keyboardNotification = KeyboardNotification(notification: notification) else { + return + } + + let animationDuration = keyboardNotification.animationDuration ?? KeyboardAnimation.defaultDuration + + let animationOptions = keyboardNotification.animationOptions(fallbackAnimationCurveValue: KeyboardAnimation.defaultAnimationCurveRawValue) + + // Reset scroll view bottom inset to zero + let scrollViewBottomInset: CGFloat = 0.0 + + UIView.animate(withDuration: animationDuration, + delay: 0.0, + options: animationOptions, animations: { + scrollView.contentInset.bottom = scrollViewBottomInset + scrollView.scrollIndicatorInsets.bottom = scrollViewBottomInset + }, completion: nil) + } +} diff --git a/Riot/Modules/Common/KeyboardAvoiding/KeyboardNotification.swift b/Riot/Modules/Common/KeyboardAvoiding/KeyboardNotification.swift new file mode 100644 index 000000000..c07af2cc4 --- /dev/null +++ b/Riot/Modules/Common/KeyboardAvoiding/KeyboardNotification.swift @@ -0,0 +1,65 @@ +/* + 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 + +/// Wrapper for the Notification userInfo values associated with a keyboard notification. +public struct KeyboardNotification { + + let userInfo: [AnyHashable: Any] + + public init?(notification: Notification) { + guard let userInfo = notification.userInfo else { + return nil + } + self.userInfo = userInfo + } + + public var keyboardFrameBegin: CGRect? { + guard let value = userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue else { + return nil + } + return value.cgRectValue + } + + public var keyboardFrameEnd: CGRect? { + guard let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else { + return nil + } + return value.cgRectValue + } + + public var animationDuration: TimeInterval? { + guard let number = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber else { + return nil + } + return number.doubleValue + } + + /// Keyboard UIViewAnimationCurve enum raw value + public var animationCurveRawValue: Int? { + guard let number = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber else { + return nil + } + return number.intValue + } + + /// Convert UIViewAnimationCurve raw value to UIViewAnimationOptions + public func animationOptions(fallbackAnimationCurveValue: Int = UIViewAnimationCurve.easeInOut.rawValue) -> UIViewAnimationOptions { + let animationCurveRawValue = self.animationCurveRawValue ?? fallbackAnimationCurveValue + return UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue << 16)) + } +} diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h index 6d710889c..9acb5c13a 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h @@ -32,6 +32,15 @@ typedef enum : NSUInteger } RecentsDataSourceMode; +/** + List the different key backup banners that could be displayed. + */ +typedef NS_ENUM(NSInteger, KeyBackupBanner) +{ + KeyBackupBannerNone, + KeyBackupBannerSetup, + KeyBackupBannerRecover +}; /** Action identifier used when the user tapped on the directory change button. @@ -46,6 +55,7 @@ extern NSString *const kRecentsDataSourceTapOnDirectoryServerChange; */ @interface RecentsDataSource : MXKInterleavedRecentsDataSource +@property (nonatomic) NSInteger keyBackupBannerSection; @property (nonatomic) NSInteger directorySection; @property (nonatomic) NSInteger invitesSection; @property (nonatomic) NSInteger favoritesSection; @@ -61,6 +71,8 @@ extern NSString *const kRecentsDataSourceTapOnDirectoryServerChange; @property (nonatomic, readonly) NSArray* lowPriorityCellDataArray; @property (nonatomic, readonly) NSArray* serverNoticeCellDataArray; +@property (nonatomic, readonly) KeyBackupBanner keyBackupBanner; + /** Set the delegate by specifying the selected display mode. */ diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index e228e2039..846ab732e 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -19,7 +19,7 @@ #import "RecentCellData.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "MXRoom+Riot.h" @@ -40,7 +40,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSourceTapOnDirectoryServerChange"; -@interface RecentsDataSource() +@interface RecentsDataSource() { NSMutableArray* invitesCellDataArray; NSMutableArray* favoriteCellDataArray; @@ -60,10 +60,13 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou // Timer to not refresh publicRoomsDirectoryDataSource on every keystroke. NSTimer *publicRoomsTriggerTimer; } + +@property (nonatomic, assign, readwrite) KeyBackupBanner keyBackupBanner; + @end @implementation RecentsDataSource -@synthesize directorySection, invitesSection, favoritesSection, peopleSection, conversationSection, lowPrioritySection, serverNoticeSection; +@synthesize directorySection, invitesSection, favoritesSection, peopleSection, conversationSection, lowPrioritySection, serverNoticeSection, keyBackupBannerSection; @synthesize hiddenCellIndexPath, droppingCellIndexPath, droppingCellBackGroundView; @synthesize invitesCellDataArray, favoriteCellDataArray, peopleCellDataArray, conversationCellDataArray, lowPriorityCellDataArray, serverNoticeCellDataArray; @@ -79,6 +82,9 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou serverNoticeCellDataArray = [[NSMutableArray alloc] init]; conversationCellDataArray = [[NSMutableArray alloc] init]; + + _keyBackupBanner = KeyBackupBannerNone; + keyBackupBannerSection = -1; directorySection = -1; invitesSection = -1; favoritesSection = -1; @@ -111,6 +117,16 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou { _recentsDataSourceMode = recentsDataSourceMode; + // Register to key backup state changes only on in home mode. + if (recentsDataSourceMode == RecentsDataSourceModeHome) + { + [self registerKeyBackupStateDidChangeNotification]; + } + else + { + [self unregisterKeyBackupStateDidChangeNotification]; + } + [self forceRefresh]; } @@ -132,6 +148,93 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou return stickyHeader; } +#pragma mark - Key backup setup banner + +- (void)registerKeyBackupStateDidChangeNotification +{ + // Check homeserver update in background + [self.mxSession.crypto.backup forceRefresh:nil failure:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBackupStateDidChangeNotification:) name:kMXKeyBackupDidStateChangeNotification object:nil]; +} + +- (void)unregisterKeyBackupStateDidChangeNotification +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKeyBackupDidStateChangeNotification object:nil]; +} + +- (void)keyBackupStateDidChangeNotification:(NSNotification*)notification +{ + [self forceRefresh]; +} + +- (void)updateKeyBackupBanner +{ + KeyBackupBanner keyBackupBanner = KeyBackupBannerNone; + + if (self.recentsDataSourceMode == RecentsDataSourceModeHome) + { + KeyBackupBannerPreferences *keyBackupBannersPreferences = KeyBackupBannerPreferences.shared; + + NSString *keyBackupVersion = self.mxSession.crypto.backup.keyBackupVersion.version; + + switch (self.mxSession.crypto.backup.state) { + case MXKeyBackupStateDisabled: + // Show key backup setup banner only if user has not hidden it once. + if (keyBackupBannersPreferences.hideSetupBanner) + { + keyBackupBanner = KeyBackupBannerNone; + } + else + { + keyBackupBanner = KeyBackupBannerSetup; + } + break; + case MXKeyBackupStateNotTrusted: + case MXKeyBackupStateWrongBackUpVersion: + // Show key backup recover banner only if user has not hidden it for the given version. + if (keyBackupVersion && [keyBackupBannersPreferences isRecoverBannerHiddenFor:keyBackupVersion]) + { + keyBackupBanner = KeyBackupBannerNone; + } + else + { + keyBackupBanner = KeyBackupBannerRecover; + } + break; + default: + keyBackupBanner = KeyBackupBannerNone; + break; + } + } + + self.keyBackupBanner = keyBackupBanner; +} + +- (void)hideKeyBackupBanner:(KeyBackupBanner)keyBackupBanner +{ + KeyBackupBannerPreferences *keyBackupBannersPreferences = KeyBackupBannerPreferences.shared; + + switch (keyBackupBanner) { + case KeyBackupBannerSetup: + keyBackupBannersPreferences.hideSetupBanner = YES; + break; + case KeyBackupBannerRecover: + { + NSString *keyBackupVersion = self.mxSession.crypto.backup.keyBackupVersion.version; + if (keyBackupVersion) + { + [keyBackupBannersPreferences hideRecoverBannerFor:keyBackupVersion]; + } + } + break; + default: + break; + } + + [self forceRefresh]; +} + #pragma mark - - (MXKSessionRecentsDataSource *)addMatrixSession:(MXSession *)mxSession @@ -156,7 +259,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou // sanity check if (matrixSession.myUser && matrixSession.myUser.userId) { - id roomTagListener = [roomTagsListenerByUserId objectForKey:matrixSession.myUser.userId]; + id roomTagListener = roomTagsListenerByUserId[matrixSession.myUser.userId]; if (roomTagListener) { @@ -204,7 +307,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou }]; - [roomTagsListenerByUserId setObject:roomTagsListener forKey:dataSource.mxSession.myUser.userId]; + roomTagsListenerByUserId[dataSource.mxSession.myUser.userId] = roomTagsListener; } } } @@ -246,7 +349,12 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou // Check whether all data sources are ready before rendering recents if (self.state == MXKDataSourceStateReady) { - directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = invitesSection = serverNoticeSection = -1; + keyBackupBannerSection = directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = invitesSection = serverNoticeSection = -1; + + if (self.keyBackupBanner != KeyBackupBannerNone) + { + self.keyBackupBannerSection = sectionsCount++; + } if (invitesCellDataArray.count > 0) { @@ -300,7 +408,11 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou NSUInteger count = 0; - if (section == favoritesSection && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_FAVORITES)) + if (section == self.keyBackupBannerSection && self.keyBackupBanner != KeyBackupBannerNone) + { + count = 1; + } + else if (section == favoritesSection && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_FAVORITES)) { count = favoriteCellDataArray.count; } @@ -345,7 +457,11 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou - (CGFloat)heightForHeaderInSection:(NSInteger)section { - if (section == directorySection && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_DIRECTORY)) + if (section == self.keyBackupBannerSection) + { + return 0.0; + } + else if (section == directorySection && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_DIRECTORY)) { return RECENTSDATASOURCE_DIRECTORY_SECTION_HEADER_HEIGHT; } @@ -415,10 +531,10 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou NSString *roomCount = [NSString stringWithFormat:@" %tu", count]; NSMutableAttributedString *mutableSectionTitle = [[NSMutableAttributedString alloc] initWithString:title - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextPrimaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; [mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount - attributes:@{NSForegroundColorAttributeName : kRiotAuxiliaryColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextSecondaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]]; sectionTitle = mutableSectionTitle; @@ -426,7 +542,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou else if (title) { sectionTitle = [[NSAttributedString alloc] initWithString:title - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextPrimaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; } @@ -469,7 +585,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou if (count) { UILabel *missedNotifAndUnreadBadgeLabel = [[UILabel alloc] init]; - missedNotifAndUnreadBadgeLabel.textColor = kRiotPrimaryBgColor; + missedNotifAndUnreadBadgeLabel.textColor = ThemeService.shared.theme.backgroundColor; missedNotifAndUnreadBadgeLabel.font = [UIFont boldSystemFontOfSize:14]; if (count > 1000) { @@ -487,7 +603,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou missedNotifAndUnreadBadgeBgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, bgViewWidth, 20)]; [missedNotifAndUnreadBadgeBgView.layer setCornerRadius:10]; - missedNotifAndUnreadBadgeBgView.backgroundColor = kRiotAuxiliaryColor; + missedNotifAndUnreadBadgeBgView.backgroundColor = ThemeService.shared.theme.headerTextSecondaryColor; [missedNotifAndUnreadBadgeBgView addSubview:missedNotifAndUnreadBadgeLabel]; missedNotifAndUnreadBadgeLabel.center = missedNotifAndUnreadBadgeBgView.center; @@ -515,8 +631,14 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou - (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame { + // No header view in key backup banner section + if (section == self.keyBackupBannerSection) + { + return nil; + } + UIView *sectionHeader = [[UIView alloc] initWithFrame:frame]; - sectionHeader.backgroundColor = kRiotSecondaryBgColor; + sectionHeader.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; NSInteger sectionBitwise = 0; UIImageView *chevronView; UIView *accessoryView; @@ -754,8 +876,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } // Apply the current UI theme. - networkLabel.textColor = kRiotPrimaryTextColor; - directoryServerLabel.textColor = kRiotSecondaryTextColor; + networkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + directoryServerLabel.textColor = ThemeService.shared.theme.textSecondaryColor; // Set the current directory server name directoryServerLabel.text = _publicRoomsDirectoryDataSource.directoryServerDisplayname; @@ -807,7 +929,14 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou return [[UITableViewCell alloc] init]; } - if (indexPath.section == directorySection) + if (indexPath.section == self.keyBackupBannerSection) + { + KeyBackupBannerCell* keyBackupBannerCell = [tableView dequeueReusableCellWithIdentifier:KeyBackupBannerCell.defaultReuseIdentifier forIndexPath:indexPath]; + [keyBackupBannerCell configureFor:self.keyBackupBanner]; + keyBackupBannerCell.delegate = self; + return keyBackupBannerCell; + } + else if (indexPath.section == directorySection) { NSIndexPath *indexPathInPublicRooms = [NSIndexPath indexPathForRow:indexPath.row inSection:0]; return [_publicRoomsDirectoryDataSource tableView:tableView cellForRowAtIndexPath:indexPathInPublicRooms]; @@ -846,7 +975,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou if (!tableViewCell) { tableViewCell = [[MXKTableViewCell alloc] init]; - tableViewCell.textLabel.textColor = kRiotSecondaryTextColor; + tableViewCell.textLabel.textColor = ThemeService.shared.theme.textSecondaryColor; tableViewCell.textLabel.font = [UIFont systemFontOfSize:15.0]; tableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; } @@ -891,42 +1020,42 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou { if (cellDataIndex < favoriteCellDataArray.count) { - cellData = [favoriteCellDataArray objectAtIndex:cellDataIndex]; + cellData = favoriteCellDataArray[cellDataIndex]; } } else if (tableSection == peopleSection) { if (cellDataIndex < peopleCellDataArray.count) { - cellData = [peopleCellDataArray objectAtIndex:cellDataIndex]; + cellData = peopleCellDataArray[cellDataIndex]; } } else if (tableSection== conversationSection) { if (cellDataIndex < conversationCellDataArray.count) { - cellData = [conversationCellDataArray objectAtIndex:cellDataIndex]; + cellData = conversationCellDataArray[cellDataIndex]; } } else if (tableSection == lowPrioritySection) { if (cellDataIndex < lowPriorityCellDataArray.count) { - cellData = [lowPriorityCellDataArray objectAtIndex:cellDataIndex]; + cellData = lowPriorityCellDataArray[cellDataIndex]; } } else if (tableSection == serverNoticeSection) { if (cellDataIndex < serverNoticeCellDataArray.count) { - cellData = [serverNoticeCellDataArray objectAtIndex:cellDataIndex]; + cellData = serverNoticeCellDataArray[cellDataIndex]; } } else if (tableSection == invitesSection) { if (cellDataIndex < invitesCellDataArray.count) { - cellData = [invitesCellDataArray objectAtIndex:cellDataIndex]; + cellData = invitesCellDataArray[cellDataIndex]; } } @@ -983,7 +1112,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou { for (int index = 0; index < cellDataArray.count; index++) { - id cellDataStoring = [cellDataArray objectAtIndex:index]; + id cellDataStoring = cellDataArray[index]; if ([roomId isEqualToString:cellDataStoring.roomSummary.roomId] && (matrixSession == cellDataStoring.roomSummary.room.mxSession)) { @@ -1109,12 +1238,14 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou _missedDirectDiscussionsCount = _missedHighlightDirectDiscussionsCount = 0; _missedGroupDiscussionsCount = _missedHighlightGroupDiscussionsCount = 0; - directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = serverNoticeSection = invitesSection = -1; + keyBackupBannerSection = directorySection = favoritesSection = peopleSection = conversationSection = lowPrioritySection = serverNoticeSection = invitesSection = -1; + + [self updateKeyBackupBanner]; if (displayedRecentsDataSourceArray.count > 0) { // FIXME manage multi accounts - MXKSessionRecentsDataSource *recentsDataSource = [displayedRecentsDataSourceArray objectAtIndex:0]; + MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray[0]; MXSession* session = recentsDataSource.mxSession; NSInteger count = recentsDataSource.numberOfCells; @@ -1592,4 +1723,11 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } } +#pragma mark - KeyBackupSetupBannerCellDelegate + +- (void)keyBackupBannerCellDidTapCloseAction:(KeyBackupBannerCell * _Nonnull)cell +{ + [self hideKeyBackupBanner:self.keyBackupBanner]; +} + @end diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index f0f54f16a..fa95b06f2 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -33,6 +33,7 @@ #import "RoomIdOrAliasTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface RecentsViewController () { @@ -62,8 +63,8 @@ // when the user selects it. UISearchBar *tableSearchBar; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -148,7 +149,7 @@ self.recentsSearchBar.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil); // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -158,18 +159,18 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Use the primary bg color for the recents table view in plain style. - self.recentsTableView.backgroundColor = kRiotPrimaryBgColor; - topview.backgroundColor = kRiotSecondaryBgColor; - self.view.backgroundColor = kRiotPrimaryBgColor; - - tableSearchBar.barStyle = self.recentsSearchBar.barStyle = kRiotDesignSearchBarStyle; - tableSearchBar.tintColor = self.recentsSearchBar.tintColor = kRiotDesignSearchBarTintColor; - + self.recentsTableView.backgroundColor = ThemeService.shared.theme.backgroundColor; + topview.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; + + [ThemeService.shared.theme applyStyleOnSearchBar:tableSearchBar]; + [ThemeService.shared.theme applyStyleOnSearchBar:self.recentsSearchBar]; + if (self.recentsTableView.dataSource) { // Force table refresh @@ -179,7 +180,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy @@ -206,10 +207,10 @@ UIApplicationDidEnterBackgroundNotificationObserver = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -867,7 +868,7 @@ }]; UIImage *actionIcon = isDirect ? [UIImage imageNamed:@"directChatOff"] : [UIImage imageNamed:@"directChatOn"]; - directAction.backgroundColor = [MXKTools convertImageToPatternColor:isDirect ? @"directChatOff" : @"directChatOn" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + directAction.backgroundColor = [MXKTools convertImageToPatternColor:isDirect ? @"directChatOff" : @"directChatOn" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:directAction atIndex:0]; @@ -881,7 +882,7 @@ }]; actionIcon = isMuted ? [UIImage imageNamed:@"notifications"] : [UIImage imageNamed:@"notificationsOff"]; - muteAction.backgroundColor = [MXKTools convertImageToPatternColor:isMuted ? @"notifications" : @"notificationsOff" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + muteAction.backgroundColor = [MXKTools convertImageToPatternColor:isMuted ? @"notifications" : @"notificationsOff" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:muteAction atIndex:0]; // Favorites management @@ -893,7 +894,7 @@ NSArray* tags = room.accountData.tags.allValues; if (tags.count) { - currentTag = [tags objectAtIndex:0]; + currentTag = tags[0]; } } @@ -906,7 +907,7 @@ }]; actionIcon = [UIImage imageNamed:@"favouriteOff"]; - action.backgroundColor = [MXKTools convertImageToPatternColor:@"favouriteOff" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + action.backgroundColor = [MXKTools convertImageToPatternColor:@"favouriteOff" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:action atIndex:0]; } else @@ -918,7 +919,7 @@ }]; actionIcon = [UIImage imageNamed:@"favourite"]; - action.backgroundColor = [MXKTools convertImageToPatternColor:@"favourite" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + action.backgroundColor = [MXKTools convertImageToPatternColor:@"favourite" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:action atIndex:0]; } @@ -931,7 +932,7 @@ }]; actionIcon = [UIImage imageNamed:@"priorityHigh"]; - action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityHigh" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityHigh" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:action atIndex:0]; } else @@ -943,7 +944,7 @@ }]; actionIcon = [UIImage imageNamed:@"priorityLow"]; - action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityLow" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityLow" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:action atIndex:0]; } @@ -954,7 +955,7 @@ }]; actionIcon = [UIImage imageNamed:@"leave"]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"leave" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"leave" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size]; [actions insertObject:leaveAction atIndex:0]; } @@ -1184,13 +1185,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -1412,7 +1413,7 @@ if (indexPath && [recentsDataSource isDraggableCellAt:indexPath]) { UITableViewCell *cell = [self.recentsTableView cellForRowAtIndexPath:indexPath]; - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // snapshot the cell UIGraphicsBeginImageContextWithOptions(cell.bounds.size, NO, 0); @@ -1808,14 +1809,13 @@ { typeof(self) self = weakSelf; - UITextField *textField = [self->currentAlert textFields].firstObject; - NSString *roomAliasOrId = textField.text; + NSString *roomAliasOrId = [self->currentAlert textFields].firstObject.text; self->currentAlert = nil; [self.activityIndicator startAnimating]; - self->currentRequest = [self.mainSession joinRoom:textField.text success:^(MXRoom *room) { + self->currentRequest = [self.mainSession joinRoom:roomAliasOrId success:^(MXRoom *room) { self->currentRequest = nil; [self.activityIndicator stopAnimating]; diff --git a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m index 48c15da08..4eb05500b 100644 --- a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m +++ b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m @@ -21,7 +21,8 @@ #import "MXEvent.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXRoomSummary+Riot.h" @@ -47,13 +48,13 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; { [super customizeTableViewCellRendering]; - self.roomTitle.textColor = kRiotPrimaryTextColor; - self.lastEventDescription.textColor = kRiotSecondaryTextColor; - self.lastEventDate.textColor = kRiotSecondaryTextColor; - self.missedNotifAndUnreadBadgeLabel.textColor = kRiotPrimaryBgColor; + self.roomTitle.textColor = ThemeService.shared.theme.textPrimaryColor; + self.lastEventDescription.textColor = ThemeService.shared.theme.textSecondaryColor; + self.lastEventDate.textColor = ThemeService.shared.theme.textSecondaryColor; + self.missedNotifAndUnreadBadgeLabel.textColor = ThemeService.shared.theme.backgroundColor; // Prepare direct room border - CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(kRiotColorGreen.CGColor, kDirectRoomBorderColorAlpha); + CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(ThemeService.shared.theme.tintColor.CGColor, kDirectRoomBorderColorAlpha); [self.directRoomBorderView.layer setCornerRadius:self.directRoomBorderView.frame.size.width / 2]; self.directRoomBorderView.clipsToBounds = YES; @@ -93,7 +94,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; { // Force the default text color for the last message (cancel highlighted message color) NSMutableAttributedString *lastEventDescription = [[NSMutableAttributedString alloc] initWithAttributedString:roomCellData.lastEventAttributedTextMessage]; - [lastEventDescription addAttribute:NSForegroundColorAttributeName value:kRiotSecondaryTextColor range:NSMakeRange(0, lastEventDescription.length)]; + [lastEventDescription addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.textSecondaryColor range:NSMakeRange(0, lastEventDescription.length)]; self.lastEventDescription.attributedText = lastEventDescription; } else @@ -108,7 +109,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; if (0 < roomCellData.notificationCount) { - self.missedNotifAndUnreadIndicator.backgroundColor = roomCellData.highlightCount ? kRiotColorPinkRed : kRiotColorGreen; + self.missedNotifAndUnreadIndicator.backgroundColor = roomCellData.highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor; self.missedNotifAndUnreadBadgeBgView.hidden = NO; self.missedNotifAndUnreadBadgeBgView.backgroundColor = self.missedNotifAndUnreadIndicator.backgroundColor; @@ -120,7 +121,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; } else { - self.missedNotifAndUnreadIndicator.backgroundColor = kRiotAuxiliaryColor; + self.missedNotifAndUnreadIndicator.backgroundColor = ThemeService.shared.theme.unreadRoomIndentColor; } // Use bold font for the room title @@ -135,7 +136,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; } else { - self.lastEventDate.textColor = kRiotSecondaryTextColor; + self.lastEventDate.textColor = ThemeService.shared.theme.textSecondaryColor; // The room title is not bold anymore if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) diff --git a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.h b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.h index a0a340863..f68d498ae 100644 --- a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.h +++ b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.h @@ -38,7 +38,7 @@ limitations under the License. @property (nonatomic) NSUInteger selectedIndex; /** - The tint color for the section header (kRiotColorGreen by default). + The tint color for the section header (ThemeService.shared.theme.accent by default). */ @property (nonatomic) UIColor *sectionHeaderTintColor; diff --git a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m index c4d5d4d12..7823d8c67 100644 --- a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m +++ b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m @@ -17,7 +17,13 @@ #import "SegmentedViewController.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" + +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif @interface SegmentedViewController () { @@ -43,8 +49,8 @@ UIView* selectedMarkerView; NSLayoutConstraint *leftMarkerViewConstraint; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -98,10 +104,10 @@ selectedMarkerView = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [super destroy]; @@ -129,8 +135,6 @@ // Setup `MXKViewControllerHandling` properties self.enableBarTintColorStatusChange = NO; - - self.sectionHeaderTintColor = kRiotColorGreen; } - (void)viewDidLoad @@ -162,7 +166,7 @@ [self createSegmentedViews]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -172,16 +176,18 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.view.backgroundColor = kRiotPrimaryBgColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; + + self.sectionHeaderTintColor = ThemeService.shared.theme.tintColor; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -243,7 +249,7 @@ // create programmatically each label UILabel *label = [[UILabel alloc] init]; - label.text = [sectionTitles objectAtIndex:index]; + label.text = sectionTitles[index]; label.font = [UIFont systemFontOfSize:17]; label.textAlignment = NSTextAlignmentCenter; label.textColor = _sectionHeaderTintColor; @@ -263,7 +269,7 @@ leftConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:[labels objectAtIndex:(index-1)] + toItem:labels[index - 1] attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]; @@ -339,7 +345,7 @@ leftMarkerViewConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:[sectionLabels objectAtIndex:_selectedIndex] + toItem:sectionLabels[_selectedIndex] attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; @@ -386,7 +392,7 @@ if (index != NSNotFound) { - UILabel* label = [sectionLabels objectAtIndex:index]; + UILabel* label = sectionLabels[index]; label.font = [UIFont systemFontOfSize:17]; } @@ -398,7 +404,7 @@ [NSLayoutConstraint deactivateConstraints:@[displayedVCTopConstraint, displayedVCLeftConstraint, displayedVCWidthConstraint, displayedVCHeightConstraint]]; } - UILabel* label = [sectionLabels objectAtIndex:_selectedIndex]; + UILabel* label = sectionLabels[_selectedIndex]; label.font = [UIFont boldSystemFontOfSize:17]; // update the marker view position @@ -407,7 +413,7 @@ leftMarkerViewConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:[sectionLabels objectAtIndex:_selectedIndex] + toItem:sectionLabels[_selectedIndex] attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; @@ -415,7 +421,7 @@ [NSLayoutConstraint activateConstraints:@[leftMarkerViewConstraint]]; // Set the new selected view controller - _selectedViewController = [viewControllers objectAtIndex:_selectedIndex]; + _selectedViewController = viewControllers[_selectedIndex]; // Make iOS invoke selectedViewController viewWillAppear when the segmented view is already visible if (isViewAppeared) diff --git a/Riot/Modules/Common/WebViewController/WebViewViewController.m b/Riot/Modules/Common/WebViewController/WebViewViewController.m index 67c5e69e5..14426ff8c 100644 --- a/Riot/Modules/Common/WebViewController/WebViewViewController.m +++ b/Riot/Modules/Common/WebViewController/WebViewViewController.m @@ -16,11 +16,12 @@ #import "WebViewViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface WebViewViewController () { - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -41,7 +42,7 @@ [super viewDidLoad]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -51,25 +52,24 @@ - (void)userInterfaceThemeDidChange { - self.view.backgroundColor = kRiotPrimaryBgColor; - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - webView.backgroundColor = kRiotPrimaryBgColor; + webView.backgroundColor = ThemeService.shared.theme.backgroundColor; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [super destroy]; diff --git a/Riot/Modules/Communities/GroupsViewController.m b/Riot/Modules/Communities/GroupsViewController.m index 04d1209a6..e96ff2aca 100644 --- a/Riot/Modules/Communities/GroupsViewController.m +++ b/Riot/Modules/Communities/GroupsViewController.m @@ -20,6 +20,7 @@ #import "GroupInviteTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface GroupsViewController () { @@ -38,8 +39,8 @@ // when the user selects it. UISearchBar *tableSearchBar; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -105,7 +106,7 @@ //[self addPlusButton]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -115,17 +116,17 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Use the primary bg color for the recents table view in plain style. - self.groupsTableView.backgroundColor = kRiotPrimaryBgColor; - topview.backgroundColor = kRiotSecondaryBgColor; - self.view.backgroundColor = kRiotPrimaryBgColor; - - tableSearchBar.barStyle = self.groupsSearchBar.barStyle = kRiotDesignSearchBarStyle; - tableSearchBar.tintColor = self.groupsSearchBar.tintColor = kRiotDesignSearchBarTintColor; + self.groupsTableView.backgroundColor = ThemeService.shared.theme.backgroundColor; + topview.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; + + [ThemeService.shared.theme applyStyleOnSearchBar:tableSearchBar]; + [ThemeService.shared.theme applyStyleOnSearchBar:self.groupsSearchBar]; if (self.groupsTableView.dataSource) { @@ -136,7 +137,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy @@ -161,10 +162,10 @@ UIApplicationDidEnterBackgroundNotificationObserver = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -203,8 +204,7 @@ }]; [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_groups", @"Vector", nil); - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorBlue; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorBlue; + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorBlue; } - (void)viewWillDisappear:(BOOL)animated @@ -219,13 +219,6 @@ [[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver]; kAppDelegateDidTapStatusBarNotificationObserver = nil; } - - if ([AppDelegate theDelegate].masterTabBarController.tabBar.tintColor == kRiotColorBlue && ![AppDelegate theDelegate].masterTabBarController.selectedGroup) - { - // Restore default tintColor - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorGreen; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorGreen; - } } - (void)viewDidAppear:(BOOL)animated @@ -431,13 +424,13 @@ { [super tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath]; - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -459,8 +452,8 @@ if (tableView.numberOfSections > 1) { sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:MXKTableViewHeaderFooterWithLabel.defaultReuseIdentifier]; - sectionHeader.mxkContentView.backgroundColor = kRiotSecondaryBgColor; - sectionHeader.mxkLabel.textColor = kRiotPrimaryTextColor; + sectionHeader.mxkContentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + sectionHeader.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; sectionHeader.mxkLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; NSString* title = [self.dataSource tableView:tableView titleForHeaderInSection:section]; @@ -469,9 +462,9 @@ { NSString *roomCount = [NSString stringWithFormat:@" %tu", count]; NSMutableAttributedString *mutableSectionTitle = [[NSMutableAttributedString alloc] initWithString:title - attributes:@{NSForegroundColorAttributeName: kRiotPrimaryTextColor}]; + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.headerTextPrimaryColor}]; [mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount - attributes:@{NSForegroundColorAttributeName: kRiotAuxiliaryColor}]]; + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.headerTextSecondaryColor}]]; sectionHeader.mxkLabel.attributedText = mutableSectionTitle; } @@ -519,7 +512,7 @@ }]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:leaveAction atIndex:0]; } @@ -719,14 +712,13 @@ // { // typeof(self) self = weakSelf; // -// UITextField *textField = [self->currentAlert textFields].firstObject; -// NSString *roomAliasOrId = textField.text; +// NSString *roomAliasOrId = [self->currentAlert textFields].firstObject.text; // // self->currentAlert = nil; // // [self.activityIndicator startAnimating]; // -// self->currentRequest = [self.mainSession joinRoom:textField.text success:^(MXRoom *room) { +// self->currentRequest = [self.mainSession joinRoom:roomAliasOrId success:^(MXRoom *room) { // // self->currentRequest = nil; // [self.activityIndicator stopAnimating]; diff --git a/Riot/Modules/Communities/Home/GroupHomeViewController.m b/Riot/Modules/Communities/Home/GroupHomeViewController.m index 247b49521..b488c7102 100644 --- a/Riot/Modules/Communities/Home/GroupHomeViewController.m +++ b/Riot/Modules/Communities/Home/GroupHomeViewController.m @@ -18,8 +18,9 @@ #import "GroupHomeViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "Tools.h" #import "MXGroup+Riot.h" @@ -35,8 +36,8 @@ */ BOOL isStatusBarHidden; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; // The options used to load long description html content. NSDictionary *options; @@ -102,7 +103,7 @@ _groupAvatarMask.userInteractionEnabled = YES; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -112,38 +113,38 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.view.backgroundColor = kRiotPrimaryBgColor; - self.mainHeaderContainer.backgroundColor = kRiotSecondaryBgColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.mainHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - _groupName.textColor = kRiotPrimaryTextColor; + _groupName.textColor = ThemeService.shared.theme.textPrimaryColor; - _groupDescription.textColor = kRiotTopicTextColor; + _groupDescription.textColor = ThemeService.shared.theme.baseTextSecondaryColor; _groupDescription.numberOfLines = 0; - self.inviteLabel.textColor = kRiotTopicTextColor; + self.inviteLabel.textColor = ThemeService.shared.theme.baseTextSecondaryColor; self.inviteLabel.numberOfLines = 0; - self.separatorView.backgroundColor = kRiotSecondaryBgColor; + self.separatorView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; [self.leftButton.layer setCornerRadius:5]; self.leftButton.clipsToBounds = YES; - self.leftButton.backgroundColor = kRiotColorBlue; + self.leftButton.backgroundColor = ThemeService.shared.riotColorBlue; [self.rightButton.layer setCornerRadius:5]; self.rightButton.clipsToBounds = YES; - self.rightButton.backgroundColor = kRiotColorBlue; + self.rightButton.backgroundColor = ThemeService.shared.riotColorBlue; if (_groupLongDescription) { - _groupLongDescription.textColor = kRiotSecondaryTextColor; - _groupLongDescription.tintColor = kRiotColorBlue; + _groupLongDescription.textColor = ThemeService.shared.theme.textSecondaryColor; + _groupLongDescription.tintColor = ThemeService.shared.riotColorBlue; // Update HTML loading options - NSUInteger bgColor = [MXKTools rgbValueWithColor:kRiotSecondaryBgColor]; + NSUInteger bgColor = [MXKTools rgbValueWithColor:ThemeService.shared.theme.headerBackgroundColor]; NSString *defaultCSS = [NSString stringWithFormat:@" \ pre,code { \ background-color: #%06lX; \ @@ -169,7 +170,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -258,10 +259,10 @@ [currentRequest cancel]; currentRequest = nil; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -480,7 +481,7 @@ _groupAvatar.layer.cornerRadius = _groupAvatar.frame.size.width / 2; _groupAvatar.clipsToBounds = YES; - _groupAvatar.defaultBackgroundColor = kRiotSecondaryBgColor; + _groupAvatar.defaultBackgroundColor = ThemeService.shared.theme.headerBackgroundColor; } - (void)refreshGroupLongDescription diff --git a/Riot/Modules/Communities/Members/GroupParticipantsViewController.m b/Riot/Modules/Communities/Members/GroupParticipantsViewController.m index ef7ba2360..fc5ec787e 100644 --- a/Riot/Modules/Communities/Members/GroupParticipantsViewController.m +++ b/Riot/Modules/Communities/Members/GroupParticipantsViewController.m @@ -17,6 +17,7 @@ #import "GroupParticipantsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "Contact.h" #import "ContactTableViewCell.h" @@ -45,8 +46,8 @@ UIAlertController *currentAlert; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -134,7 +135,7 @@ //[self addAddParticipantButton]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -144,24 +145,24 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; + _searchBarHeaderBorder.backgroundColor = ThemeService.shared.theme.headerBorderColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; // Update the gradient view above the screen CGFloat white = 1.0; - [kRiotPrimaryBgColor getWhite:&white alpha:nil]; + [ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil]; CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor; CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor; - tableViewMaskLayer.colors = [NSArray arrayWithObjects:(__bridge id)transparentWhiteColor, (__bridge id)transparentWhiteColor, (__bridge id)opaqueWhiteColor, nil]; + tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor]; if (self.tableView.dataSource) { @@ -171,7 +172,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } // This method is called when the viewcontroller is added or removed from a container view controller. @@ -185,10 +186,10 @@ // Release the potential pushed view controller [self releasePushedViewController]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (currentAlert) @@ -443,20 +444,19 @@ // Add blur mask programmatically tableViewMaskLayer = [CAGradientLayer layer]; - // Consider the grayscale components of the kRiotPrimaryBgColor. + // Consider the grayscale components of the ThemeService.shared.theme.backgroundColor. CGFloat white = 1.0; - [kRiotPrimaryBgColor getWhite:&white alpha:nil]; + [ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil]; CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor; CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor; - tableViewMaskLayer.colors = [NSArray arrayWithObjects:(__bridge id)transparentWhiteColor, (__bridge id)transparentWhiteColor, (__bridge id)opaqueWhiteColor, nil]; + tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor]; // display a gradient to the rencents bottom (20% of the bottom of the screen) - tableViewMaskLayer.locations = [NSArray arrayWithObjects: - [NSNumber numberWithFloat:0], - [NSNumber numberWithFloat:0.85], - [NSNumber numberWithFloat:1.0], nil]; + tableViewMaskLayer.locations = @[@0.0F, + @0.85F, + @1.0F]; tableViewMaskLayer.bounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); tableViewMaskLayer.anchorPoint = CGPointZero; @@ -540,11 +540,11 @@ // List all the participants matrix user id to ignore them during the contacts search. for (Contact *contact in actualParticipants) { - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxGroupUser.userId]; + contactsDataSource.ignoredContactsByMatrixId[contact.mxGroupUser.userId] = contact; } for (Contact *contact in invitedParticipants) { - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxGroupUser.userId]; + contactsDataSource.ignoredContactsByMatrixId[contact.mxGroupUser.userId] = contact; } [contactsPickerViewController showSearch:YES]; @@ -877,13 +877,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -914,8 +914,8 @@ if (section == invitedSection) { sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:MXKTableViewHeaderFooterWithLabel.defaultReuseIdentifier]; - sectionHeader.mxkContentView.backgroundColor = kRiotSecondaryBgColor; - sectionHeader.mxkLabel.textColor = kRiotPrimaryTextColor; + sectionHeader.mxkContentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + sectionHeader.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; sectionHeader.mxkLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; sectionHeader.mxkLabel.text = NSLocalizedStringFromTable(@"group_participants_invited_section", @"Vector", nil); @@ -985,7 +985,7 @@ }]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:leaveAction atIndex:0]; } @@ -1198,19 +1198,19 @@ - (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar { // bar tint color - searchBar.barTintColor = searchBar.tintColor = kRiotColorBlue; - searchBar.tintColor = kRiotColorBlue; + searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.riotColorBlue; + searchBar.tintColor = ThemeService.shared.riotColorBlue; // FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals. // text color UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"]; - searchBarTextField.textColor = kRiotSecondaryTextColor; + searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor; // Magnifying glass icon. UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView; leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - leftImageView.tintColor = kRiotColorBlue; + leftImageView.tintColor = ThemeService.shared.riotColorBlue; // remove the gray background color UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"]; @@ -1221,8 +1221,8 @@ // place holder searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), - NSUnderlineColorAttributeName: kRiotColorBlue, - NSForegroundColorAttributeName: kRiotColorBlue}]; + NSUnderlineColorAttributeName: ThemeService.shared.riotColorBlue, + NSForegroundColorAttributeName: ThemeService.shared.riotColorBlue}]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText diff --git a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m index dfaaf1243..9eb349d5b 100644 --- a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m +++ b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m @@ -17,6 +17,7 @@ #import "GroupRoomsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "GroupRoomTableViewCell.h" @@ -31,8 +32,8 @@ // The current pushed view controller UIViewController *pushedViewController; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -115,7 +116,7 @@ [self.tableView registerClass:GroupRoomTableViewCell.class forCellReuseIdentifier:@"RoomTableViewCellId"]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -125,16 +126,16 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; + _searchBarHeaderBorder.backgroundColor = ThemeService.shared.theme.headerBorderColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -145,7 +146,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } // This method is called when the viewcontroller is added or removed from a container view controller. @@ -159,10 +160,10 @@ // Release the potential pushed view controller [self releasePushedViewController]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } _group = nil; @@ -486,13 +487,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -587,7 +588,7 @@ // // }]; // -// leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; +// leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; // [actions insertObject:leaveAction atIndex:0]; // // return actions; @@ -598,19 +599,19 @@ - (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar { // bar tint color - searchBar.barTintColor = searchBar.tintColor = kRiotColorBlue; - searchBar.tintColor = kRiotColorBlue; + searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.riotColorBlue; + searchBar.tintColor = ThemeService.shared.riotColorBlue; // FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals. // text color UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"]; - searchBarTextField.textColor = kRiotSecondaryTextColor; + searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor; // Magnifying glass icon. UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView; leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - leftImageView.tintColor = kRiotColorBlue; + leftImageView.tintColor = ThemeService.shared.riotColorBlue; // remove the gray background color UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"]; @@ -621,8 +622,8 @@ // place holder searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), - NSUnderlineColorAttributeName: kRiotColorBlue, - NSForegroundColorAttributeName: kRiotColorBlue}]; + NSUnderlineColorAttributeName: ThemeService.shared.riotColorBlue, + NSForegroundColorAttributeName: ThemeService.shared.riotColorBlue}]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText diff --git a/Riot/Modules/Communities/Rooms/Views/GroupRoomTableViewCell.m b/Riot/Modules/Communities/Rooms/Views/GroupRoomTableViewCell.m index ea0d153b4..0f11758c8 100644 --- a/Riot/Modules/Communities/Rooms/Views/GroupRoomTableViewCell.m +++ b/Riot/Modules/Communities/Rooms/Views/GroupRoomTableViewCell.m @@ -19,7 +19,8 @@ #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation GroupRoomTableViewCell @@ -36,8 +37,8 @@ { [super customizeTableViewCellRendering]; - self.roomDisplayName.textColor = kRiotPrimaryTextColor; - self.roomTopic.textColor = kRiotSecondaryTextColor; + self.roomDisplayName.textColor = ThemeService.shared.theme.textPrimaryColor; + self.roomTopic.textColor = ThemeService.shared.theme.textSecondaryColor; _roomAvatar.defaultBackgroundColor = [UIColor clearColor]; } diff --git a/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.m b/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.m index 08bf19492..1b885599e 100644 --- a/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.m +++ b/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.m @@ -21,6 +21,7 @@ #import "GroupRoomsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface GroupDetailsViewController () { @@ -71,7 +72,7 @@ self.enableBarTintColorStatusChange = NO; self.rageShakeManager = [RageShakeManager sharedManager]; - self.sectionHeaderTintColor = kRiotColorBlue; + self.sectionHeaderTintColor = ThemeService.shared.riotColorBlue; // Keep visible the status bar by default. isStatusBarHidden = NO; @@ -121,7 +122,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -143,42 +144,6 @@ [super viewWillDisappear:animated]; } -- (void)viewDidLayoutSubviews -{ - [super viewDidLayoutSubviews]; - - // Customize the navigation bar tint color - self.navigationController.navigationBar.tintColor = kRiotColorBlue; - - // Consider the case where the view controller is embedded inside a collapsed split view controller. - if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) - { - UINavigationController *mainNavigationController = self.splitViewController.viewControllers.firstObject; - mainNavigationController.navigationBar.tintColor = kRiotColorBlue; - } -} - -- (void)destroy -{ - // Restore the default tintColor of the main navigation controller. - if (self.navigationController.navigationBar.tintColor == kRiotColorBlue) - { - self.navigationController.navigationBar.tintColor = kRiotColorGreen; - } - - // Check whether the current view controller is embedded inside a collapsed split view controller. - if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) - { - UINavigationController *mainNavigationController = self.splitViewController.viewControllers.firstObject; - if (mainNavigationController.navigationBar.tintColor == kRiotColorBlue) - { - mainNavigationController.navigationBar.tintColor = kRiotColorGreen; - } - } - - [super destroy]; -} - - (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession { _group = group; diff --git a/Riot/Modules/Communities/Views/GroupInviteTableViewCell.m b/Riot/Modules/Communities/Views/GroupInviteTableViewCell.m index 351d5f086..16fea90a2 100644 --- a/Riot/Modules/Communities/Views/GroupInviteTableViewCell.m +++ b/Riot/Modules/Communities/Views/GroupInviteTableViewCell.m @@ -16,7 +16,8 @@ #import "GroupInviteTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #pragma mark - Constant definitions @@ -54,10 +55,10 @@ NSString *const kGroupInviteTableViewCellRoomKey = @"kGroupInviteTableViewCellRo { [super customizeTableViewCellRendering]; - self.leftButton.backgroundColor = kRiotColorBlue; - self.rightButton.backgroundColor = kRiotColorBlue; + self.leftButton.backgroundColor = ThemeService.shared.riotColorBlue; + self.rightButton.backgroundColor = ThemeService.shared.riotColorBlue; - self.noticeBadgeView.backgroundColor = kRiotColorPinkRed; + self.noticeBadgeView.backgroundColor = ThemeService.shared.theme.noticeColor; } - (void)onDeclinePressed:(id)sender diff --git a/Riot/Modules/Communities/Views/GroupTableViewCell.m b/Riot/Modules/Communities/Views/GroupTableViewCell.m index 54246f7e8..a8d197120 100644 --- a/Riot/Modules/Communities/Views/GroupTableViewCell.m +++ b/Riot/Modules/Communities/Views/GroupTableViewCell.m @@ -16,7 +16,8 @@ #import "GroupTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXGroup+Riot.h" @@ -40,13 +41,13 @@ { [super customizeTableViewCellRendering]; - self.groupName.textColor = kRiotPrimaryTextColor; - self.groupDescription.textColor = kRiotSecondaryTextColor; - self.memberCount.textColor = kRiotSecondaryTextColor; + self.groupName.textColor = ThemeService.shared.theme.textPrimaryColor; + self.groupDescription.textColor = ThemeService.shared.theme.textSecondaryColor; + self.memberCount.textColor = ThemeService.shared.theme.textSecondaryColor; if (self.missedNotifAndUnreadBadgeLabel) { - self.missedNotifAndUnreadBadgeLabel.textColor = kRiotPrimaryBgColor; + self.missedNotifAndUnreadBadgeLabel.textColor = ThemeService.shared.theme.backgroundColor; } self.groupAvatar.defaultBackgroundColor = [UIColor clearColor]; diff --git a/Riot/Modules/Contacts/ContactsTableViewController.m b/Riot/Modules/Contacts/ContactsTableViewController.m index c5f199fd5..558813569 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.m +++ b/Riot/Modules/Contacts/ContactsTableViewController.m @@ -20,6 +20,7 @@ #import "UIViewController+RiotSearch.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #define CONTACTS_TABLEVC_LOCALCONTACTS_BITWISE 0x01 #define CONTACTS_TABLEVC_USERDIRECTORY_BITWISE 0x02 @@ -35,9 +36,9 @@ id kAppDelegateDidTapStatusBarNotificationObserver; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -93,7 +94,7 @@ self.contactsTableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -103,12 +104,12 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.contactsTableView.backgroundColor; if (self.contactsTableView.dataSource) @@ -119,7 +120,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)didReceiveMemoryWarning @@ -132,10 +133,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -287,13 +288,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m index b0ac22827..c77685d09 100644 --- a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m +++ b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m @@ -17,7 +17,8 @@ #import "ContactsDataSource.h" #import "ContactTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #define CONTACTSDATASOURCE_LOCALCONTACTS_BITWISE 0x01 #define CONTACTSDATASOURCE_USERDIRECTORY_BITWISE 0x02 @@ -375,7 +376,7 @@ NSArray *identifiers = contact.matrixIdentifiers; if (identifiers.count) { - if ([_ignoredContactsByMatrixId objectForKey:identifiers.firstObject]) + if (_ignoredContactsByMatrixId[identifiers.firstObject]) { [unfilteredLocalContacts removeObjectAtIndex:index]; continue; @@ -396,7 +397,7 @@ MXKEmail *email = emails.firstObject; // Trick: ignore @facebook.com email addresses from the results - facebook have discontinued that service... - if ([_ignoredContactsByEmail objectForKey:email.emailAddress] || [email.emailAddress hasSuffix:@"@facebook.com"]) + if (_ignoredContactsByEmail[email.emailAddress] || [email.emailAddress hasSuffix:@"@facebook.com"]) { [unfilteredLocalContacts removeObjectAtIndex:index]; continue; @@ -434,7 +435,7 @@ { for (NSString *userId in identifiers) { - if ([_ignoredContactsByMatrixId objectForKey:userId] == nil) + if (_ignoredContactsByMatrixId[userId] == nil) { MXKContact *splitContact = [[MXKContact alloc] initMatrixContactWithDisplayName:contact.displayName andMatrixID:userId]; [unfilteredMatrixContacts addObject:splitContact]; @@ -444,7 +445,7 @@ else if (identifiers.count) { NSString *userId = identifiers.firstObject; - if ([_ignoredContactsByMatrixId objectForKey:userId] == nil) + if (_ignoredContactsByMatrixId[userId] == nil) { [unfilteredMatrixContacts addObject:contact]; } @@ -594,7 +595,7 @@ if (!tableViewCell) { tableViewCell = [[MXKTableViewCell alloc] init]; - tableViewCell.textLabel.textColor = kRiotSecondaryTextColor; + tableViewCell.textLabel.textColor = ThemeService.shared.theme.textSecondaryColor; tableViewCell.textLabel.font = [UIFont systemFontOfSize:15.0]; tableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; } @@ -741,10 +742,10 @@ NSString *roomCount = [NSString stringWithFormat:roomCountFormat, count]; NSMutableAttributedString *mutableSectionTitle = [[NSMutableAttributedString alloc] initWithString:title - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextPrimaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; [mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount - attributes:@{NSForegroundColorAttributeName : kRiotAuxiliaryColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextSecondaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]]; sectionTitle = mutableSectionTitle; @@ -752,7 +753,7 @@ else if (title) { sectionTitle = [[NSAttributedString alloc] initWithString:title - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.headerTextPrimaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; } @@ -766,7 +767,7 @@ NSInteger sectionBitwise = 0; sectionHeader = [[UIView alloc] initWithFrame:frame]; - sectionHeader.backgroundColor = kRiotSecondaryBgColor; + sectionHeader.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; frame.origin.x = 20; frame.origin.y = 5; @@ -969,7 +970,7 @@ } // Apply UI theme - checkboxLabel.textColor = kRiotPrimaryTextColor; + checkboxLabel.textColor = ThemeService.shared.theme.textPrimaryColor; // Set the right value of the tick box localContactsCheckbox.image = hideNonMatrixEnabledContacts ? [UIImage imageNamed:@"selection_tick"] : [UIImage imageNamed:@"selection_untick"]; diff --git a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m index 3c40ccb57..dedcb530a 100644 --- a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m +++ b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m @@ -19,6 +19,7 @@ #import "ContactDetailsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "RoomMemberTitleView.h" @@ -76,9 +77,9 @@ UIAlertController *currentAlert; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; /** The current visibility of the status bar in this view controller. @@ -220,7 +221,7 @@ }]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -230,16 +231,16 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.headerView.backgroundColor = kRiotSecondaryBgColor; - self.contactNameLabel.textColor = kRiotPrimaryTextColor; - self.contactStatusLabel.textColor = kRiotColorGreen; + self.headerView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.contactNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.contactStatusLabel.textColor = ThemeService.shared.theme.tintColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -250,7 +251,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -321,10 +322,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (roomCreationRequest) @@ -727,15 +728,15 @@ if (indexPath.row < actionsArray.count) { - NSNumber *actionNumber = [actionsArray objectAtIndex:indexPath.row]; + NSNumber *actionNumber = actionsArray[indexPath.row]; NSString *title = [self actionButtonTitle:actionNumber.unsignedIntegerValue]; [cellWithButton.mxkButton setTitle:title forState:UIControlStateNormal]; [cellWithButton.mxkButton setTitle:title forState:UIControlStateHighlighted]; - [cellWithButton.mxkButton setTitleColor:kRiotPrimaryTextColor forState:UIControlStateNormal]; - [cellWithButton.mxkButton setTitleColor:kRiotPrimaryTextColor forState:UIControlStateHighlighted]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateNormal]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateHighlighted]; [cellWithButton.mxkButton addTarget:self action:@selector(onActionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; @@ -778,13 +779,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Contacts/Details/Views/RoomTableViewCell.m b/Riot/Modules/Contacts/Details/Views/RoomTableViewCell.m index 394d2b3e9..f715c5391 100644 --- a/Riot/Modules/Contacts/Details/Views/RoomTableViewCell.m +++ b/Riot/Modules/Contacts/Details/Views/RoomTableViewCell.m @@ -17,7 +17,9 @@ #import "RoomTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" + #import "MXRoomSummary+Riot.h" #pragma mark - Defines & Constants @@ -33,10 +35,10 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; { [super customizeTableViewCellRendering]; - self.titleLabel.textColor = kRiotPrimaryTextColor; + self.titleLabel.textColor = ThemeService.shared.theme.textPrimaryColor; // Prepare direct room border - CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(kRiotColorGreen.CGColor, kDirectRoomBorderColorAlpha); + CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(ThemeService.shared.theme.tintColor.CGColor, kDirectRoomBorderColorAlpha); [self.directRoomBorderView.layer setCornerRadius:self.directRoomBorderView.frame.size.width / 2]; self.directRoomBorderView.clipsToBounds = YES; diff --git a/Riot/Modules/Contacts/Views/ContactTableViewCell.m b/Riot/Modules/Contacts/Views/ContactTableViewCell.m index 1259162ed..f55ce88e9 100644 --- a/Riot/Modules/Contacts/Views/ContactTableViewCell.m +++ b/Riot/Modules/Contacts/Views/ContactTableViewCell.m @@ -19,7 +19,8 @@ #import -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "AvatarGenerator.h" #import "Tools.h" @@ -52,8 +53,8 @@ [super customizeTableViewCellRendering]; // apply the vector colours - self.contactDisplayNameLabel.textColor = kRiotPrimaryTextColor; - self.contactInformationLabel.textColor = kRiotSecondaryTextColor; + self.contactDisplayNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.contactInformationLabel.textColor = ThemeService.shared.theme.textSecondaryColor; // Clear the default background color of a MXKImageView instance self.thumbnailView.defaultBackgroundColor = [UIColor clearColor]; @@ -222,11 +223,11 @@ } else if ((!contact.isMatrixContact && contact.phoneNumbers.count && !contact.emailAddresses.count)) { - image = [AvatarGenerator imageFromText:@"#" withBackgroundColor:kRiotColorGreen]; + image = [AvatarGenerator imageFromText:@"#" withBackgroundColor:ThemeService.shared.theme.tintColor]; } else { - image = [AvatarGenerator imageFromText:@"@" withBackgroundColor:kRiotColorGreen]; + image = [AvatarGenerator imageFromText:@"@" withBackgroundColor:ThemeService.shared.theme.tintColor]; } } diff --git a/Riot/Modules/EncryptionInfo/EncryptionInfoView.m b/Riot/Modules/EncryptionInfo/EncryptionInfoView.m index fdc6143c7..bcc35656e 100644 --- a/Riot/Modules/EncryptionInfo/EncryptionInfoView.m +++ b/Riot/Modules/EncryptionInfo/EncryptionInfoView.m @@ -17,7 +17,8 @@ #import "EncryptionInfoView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation EncryptionInfoView @@ -27,13 +28,13 @@ { [super customizeViewRendering]; - self.backgroundColor = kRiotSecondaryBgColor; - self.textView.backgroundColor = kRiotPrimaryBgColor; - self.defaultTextColor = kRiotPrimaryTextColor; - self.cancelButton.tintColor = kRiotColorGreen; - self.verifyButton.tintColor = kRiotColorGreen; - self.blockButton.tintColor = kRiotColorGreen; - self.confirmVerifyButton.tintColor = kRiotColorGreen; + self.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.textView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.defaultTextColor = ThemeService.shared.theme.textPrimaryColor; + self.cancelButton.tintColor = ThemeService.shared.theme.tintColor; + self.verifyButton.tintColor = ThemeService.shared.theme.tintColor; + self.blockButton.tintColor = ThemeService.shared.theme.tintColor; + self.confirmVerifyButton.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Favorites/FavouritesViewController.m b/Riot/Modules/Favorites/FavouritesViewController.m index a5352cb8a..c01d59cc0 100644 --- a/Riot/Modules/Favorites/FavouritesViewController.m +++ b/Riot/Modules/Favorites/FavouritesViewController.m @@ -55,8 +55,7 @@ [super viewWillAppear:animated]; [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_favourites", @"Vector", nil); - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorIndigo; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorIndigo; + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorIndigo; if (recentsDataSource) { @@ -66,18 +65,6 @@ } } -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - - if ([AppDelegate theDelegate].masterTabBarController.tabBar.tintColor == kRiotColorIndigo) - { - // Restore default tintColor - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorGreen; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorGreen; - } -} - - (void)dealloc { diff --git a/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m b/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m index 4c87b10a5..df95e8ff8 100644 --- a/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m +++ b/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m @@ -16,7 +16,7 @@ #import "UnifiedSearchRecentsDataSource.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "RoomIdOrAliasTableViewCell.h" #import "DirectoryRecentTableViewCell.h" diff --git a/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m b/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m index 6cf51a715..612297f11 100644 --- a/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m +++ b/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m @@ -18,6 +18,7 @@ #import "HomeFilesSearchViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "HomeViewController.h" @@ -29,9 +30,9 @@ @interface HomeFilesSearchViewController() { /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -60,7 +61,7 @@ self.searchTableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -70,15 +71,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.searchTableView.backgroundColor; - self.noResultsLabel.textColor = kRiotPrimaryBgColor; + self.noResultsLabel.textColor = ThemeService.shared.theme.backgroundColor; if (self.searchTableView.dataSource) { @@ -88,17 +89,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -152,13 +153,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m index 29f8ccb5d..b853631cd 100644 --- a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m @@ -18,7 +18,8 @@ #import "FilesSearchTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation FilesSearchTableViewCell @synthesize delegate, mxkCellData; @@ -27,11 +28,11 @@ { [super customizeTableViewCellRendering]; - self.title.textColor = kRiotPrimaryTextColor; + self.title.textColor = ThemeService.shared.theme.textPrimaryColor; - self.message.textColor = kRiotSecondaryTextColor; + self.message.textColor = ThemeService.shared.theme.textSecondaryColor; - self.date.tintColor = kRiotSecondaryTextColor; + self.date.tintColor = ThemeService.shared.theme.textSecondaryColor; } + (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth @@ -75,7 +76,7 @@ if (bubbleData.isAttachmentWithThumbnail) { - self.attachmentImageView.backgroundColor = kRiotPrimaryBgColor; + self.attachmentImageView.backgroundColor = ThemeService.shared.theme.backgroundColor; [self.attachmentImageView setAttachmentThumb:bubbleData.attachment]; } diff --git a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m index 83bf82ed4..4c69eab0f 100644 --- a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m +++ b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m @@ -22,7 +22,8 @@ #import "RoomBubbleCellData.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXKRoomBubbleTableViewCell+Riot.h" @@ -65,7 +66,7 @@ if (cellData) { // Highlight the search pattern - [cellData highlightPatternInTextMessage:self.searchText withForegroundColor:kRiotColorGreen andFont:patternFont]; + [cellData highlightPatternInTextMessage:self.searchText withForegroundColor:ThemeService.shared.theme.tintColor andFont:patternFont]; // Use profile information as data to display MXSearchUserProfile *userProfile = result.context.profileInfo[result.result.sender]; diff --git a/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m b/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m index d239acbc6..dc55572a9 100644 --- a/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m +++ b/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m @@ -18,6 +18,7 @@ #import "HomeMessagesSearchViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "HomeViewController.h" @@ -33,9 +34,9 @@ @interface HomeMessagesSearchViewController () { /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -67,7 +68,7 @@ self.searchTableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -77,15 +78,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.searchTableView.backgroundColor; - self.noResultsLabel.textColor = kRiotPrimaryBgColor; + self.noResultsLabel.textColor = ThemeService.shared.theme.backgroundColor; if (self.searchTableView.dataSource) { @@ -95,17 +96,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -195,13 +196,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultAttachmentBubbleCell.m b/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultAttachmentBubbleCell.m index 1cf9379ea..2423473ed 100644 --- a/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultAttachmentBubbleCell.m +++ b/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultAttachmentBubbleCell.m @@ -17,7 +17,8 @@ #import "MessagesSearchResultAttachmentBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation MessagesSearchResultAttachmentBubbleCell @@ -25,11 +26,11 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.roomNameLabel.textColor = kRiotSecondaryTextColor; + self.roomNameLabel.textColor = ThemeService.shared.theme.textSecondaryColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultTextMsgBubbleCell.m b/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultTextMsgBubbleCell.m index 8359cc26e..ea0e14502 100644 --- a/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultTextMsgBubbleCell.m +++ b/Riot/Modules/GlobalSearch/Messages/Views/MessagesSearchResultTextMsgBubbleCell.m @@ -17,7 +17,8 @@ #import "MessagesSearchResultTextMsgBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation MessagesSearchResultTextMsgBubbleCell @@ -25,11 +26,11 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.roomNameLabel.textColor = kRiotSecondaryTextColor; + self.roomNameLabel.textColor = ThemeService.shared.theme.textSecondaryColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/GlobalSearch/Rooms/DirectoryViewController.m b/Riot/Modules/GlobalSearch/Rooms/DirectoryViewController.m index 90bb1bfd5..d4c420694 100644 --- a/Riot/Modules/GlobalSearch/Rooms/DirectoryViewController.m +++ b/Riot/Modules/GlobalSearch/Rooms/DirectoryViewController.m @@ -20,6 +20,7 @@ #import "PublicRoomsDirectoryDataSource.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface DirectoryViewController () { @@ -31,8 +32,8 @@ // The animated view displayed at the table view bottom when paginating UIView* footerSpinnerView; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -60,7 +61,7 @@ self.tableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -70,12 +71,12 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -86,17 +87,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -156,13 +157,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m b/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m index 3d6145a67..5713ca837 100644 --- a/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m +++ b/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m @@ -32,6 +32,7 @@ #import "FilesSearchCellData.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "GBDeviceInfo_iOS.h" @@ -114,7 +115,7 @@ UIImageView *backgroundImageView = self.backgroundImageView; if (backgroundImageView) { - UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor]; + UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; backgroundImageView.image = image; } } diff --git a/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m b/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m index 81e47d81c..0ebf282f1 100644 --- a/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m @@ -19,7 +19,8 @@ #import "PublicRoomsDirectoryDataSource.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation DirectoryRecentTableViewCell @@ -29,8 +30,8 @@ { [super customizeTableViewCellRendering]; - self.titleLabel.textColor = kRiotPrimaryTextColor; - self.descriptionLabel.textColor = kRiotSecondaryTextColor; + self.titleLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.descriptionLabel.textColor = ThemeService.shared.theme.textSecondaryColor; } - (void)render:(PublicRoomsDirectoryDataSource *)publicRoomsDirectoryDataSource diff --git a/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m b/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m index bc52e06df..44f788e91 100644 --- a/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m @@ -18,7 +18,8 @@ #import "RoomIdOrAliasTableViewCell.h" #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIdOrAliasTableViewCell @@ -28,7 +29,7 @@ { [super customizeTableViewCellRendering]; - self.titleLabel.textColor = kRiotPrimaryTextColor; + self.titleLabel.textColor = ThemeService.shared.theme.textPrimaryColor; } - (void)layoutSubviews @@ -48,7 +49,8 @@ } else { - self.avatarImageView.image = [UIImage imageNamed:@"placeholder"]; + self.avatarImageView.image = [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor]; } self.titleLabel.text = roomIdOrAlias; diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index 8c261b545..8a63a4559 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -18,6 +18,7 @@ #import "HomeViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "RecentsDataSource.h" @@ -26,7 +27,7 @@ #import "MXRoom+Riot.h" -@interface HomeViewController () +@interface HomeViewController () { RecentsDataSource *recentsDataSource; @@ -39,6 +40,11 @@ // We store this value to prevent the collection view from scrolling to the beginning (observed on iOS < 10). CGFloat selectedCollectionViewContentOffset; } + +@property (nonatomic, strong) KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; +@property (nonatomic, strong) KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter; +@property (nonatomic, strong) KeyBackupBannerCell *keyBackupBannerPrototypeCell; + @end @implementation HomeViewController @@ -70,6 +76,9 @@ // Register table view cell used for rooms collection. [self.recentsTableView registerClass:TableViewCellWithCollectionView.class forCellReuseIdentifier:TableViewCellWithCollectionView.defaultReuseIdentifier]; + + // Register key backup banner cells + [self.recentsTableView registerNib:KeyBackupBannerCell.nib forCellReuseIdentifier:KeyBackupBannerCell.defaultReuseIdentifier]; // Change the table data source. It must be the home view controller itself. self.recentsTableView.dataSource = self; @@ -80,8 +89,10 @@ [super viewWillAppear:animated]; [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_home", @"Vector", nil); - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorGreen; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorGreen; + + [ThemeService.shared.theme applyStyleOnNavigationBar:[AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar]; + + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor; if (recentsDataSource) { @@ -134,6 +145,39 @@ } } +- (KeyBackupBannerCell *)keyBackupBannerPrototypeCell +{ + if (!_keyBackupBannerPrototypeCell) + { + _keyBackupBannerPrototypeCell = [self.recentsTableView dequeueReusableCellWithIdentifier:KeyBackupBannerCell.defaultReuseIdentifier]; + } + return _keyBackupBannerPrototypeCell; +} + +- (void)presentKeyBackupSetup +{ + KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + keyBackupSetupCoordinatorBridgePresenter.delegate = self; + + [keyBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES]; + + self.keyBackupSetupCoordinatorBridgePresenter = keyBackupSetupCoordinatorBridgePresenter; +} + +- (void)presentKeyBackupRecover +{ + MXKeyBackupVersion *keyBackupVersion = self.mainSession.crypto.backup.keyBackupVersion; + if (keyBackupVersion) + { + KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter = [[KeyBackupRecoverCoordinatorBridgePresenter alloc] initWithSession:self.mainSession keyBackupVersion:keyBackupVersion]; + keyBackupRecoverCoordinatorBridgePresenter.delegate = self; + + [keyBackupRecoverCoordinatorBridgePresenter presentFrom:self animated:YES]; + + self.keyBackupRecoverCoordinatorBridgePresenter = keyBackupRecoverCoordinatorBridgePresenter; + } +} + #pragma mark - Override RecentsViewController - (void)displayList:(MXKRecentsDataSource *)listDataSource @@ -259,7 +303,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if ((indexPath.section == recentsDataSource.conversationSection && !recentsDataSource.conversationCellDataArray.count) - || (indexPath.section == recentsDataSource.peopleSection && !recentsDataSource.peopleCellDataArray.count)) + || (indexPath.section == recentsDataSource.peopleSection && !recentsDataSource.peopleCellDataArray.count) + || (indexPath.section == recentsDataSource.keyBackupBannerSection)) { return [recentsDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; } @@ -301,7 +346,7 @@ NSArray* tags = room.accountData.tags.allValues; if (tags.count) { - currentTag = [tags objectAtIndex:0]; + currentTag = tags[0]; } } @@ -335,6 +380,32 @@ { return [recentsDataSource cellHeightAtIndexPath:indexPath]; } + else if (indexPath.section == recentsDataSource.keyBackupBannerSection) + { + CGFloat height = 0.0; + KeyBackupBannerCell *sizingCell = self.keyBackupBannerPrototypeCell; + + [sizingCell configureFor:recentsDataSource.keyBackupBanner]; + + [sizingCell layoutIfNeeded]; + + CGSize fittingSize = UILayoutFittingCompressedSize; + CGFloat tableViewWidth = CGRectGetWidth(tableView.frame); + CGFloat safeAreaWidth; + + if (@available(iOS 11.0, *)) { + // Take safe area into account + safeAreaWidth = MAX(tableView.safeAreaInsets.left, tableView.safeAreaInsets.right); + } else { + safeAreaWidth = 0; + } + + fittingSize.width = tableViewWidth - safeAreaWidth; + + height = [sizingCell systemLayoutSizeFittingSize:fittingSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel].height; + + return height; + } // Retrieve the fixed height of the collection view cell used to display a room. CGFloat height = [RoomCollectionViewCell defaultCellSize].height + 1; @@ -349,6 +420,36 @@ return height; } +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section +{ + // No header in key banner section + if (section == recentsDataSource.keyBackupBannerSection) + { + return 0.0; + } + else + { + return [super tableView:tableView heightForHeaderInSection:section]; + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section == recentsDataSource.keyBackupBannerSection) + { + switch (recentsDataSource.keyBackupBanner) { + case KeyBackupBannerSetup: + [self presentKeyBackupSetup]; + break; + case KeyBackupBannerRecover: + [self presentKeyBackupRecover]; + break; + default: + break; + } + } +} + #pragma mark - UICollectionViewDataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section @@ -413,7 +514,7 @@ } } - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; return cell; } @@ -594,4 +695,28 @@ [self leaveEditedRoom]; } +#pragma mark - KeyBackupSetupCoordinatorBridgePresenterDelegate + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter * _Nonnull)keyBackupSetupCoordinatorBridgePresenter +{ + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:YES]; + self.keyBackupSetupCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter * _Nonnull)keyBackupSetupCoordinatorBridgePresenter +{ + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:YES]; + self.keyBackupSetupCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidCancel:(KeyBackupRecoverCoordinatorBridgePresenter * _Nonnull)keyBackupRecoverCoordinatorBridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:YES]; + self.keyBackupRecoverCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCoordinatorBridgePresenter * _Nonnull)keyBackupRecoverCoordinatorBridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:YES]; + self.keyBackupRecoverCoordinatorBridgePresenter = nil; +} + @end diff --git a/Riot/Modules/Home/Views/RoomCollectionViewCell.m b/Riot/Modules/Home/Views/RoomCollectionViewCell.m index 17519e728..d555ee4c9 100644 --- a/Riot/Modules/Home/Views/RoomCollectionViewCell.m +++ b/Riot/Modules/Home/Views/RoomCollectionViewCell.m @@ -18,7 +18,8 @@ #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXRoomSummary+Riot.h" @@ -65,13 +66,13 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; { [super customizeCollectionViewCellRendering]; - self.roomTitle.textColor = kRiotPrimaryTextColor; - self.roomTitle1.textColor = kRiotPrimaryTextColor; - self.roomTitle2.textColor = kRiotPrimaryTextColor; - self.missedNotifAndUnreadBadgeLabel.textColor = kRiotPrimaryBgColor; + self.roomTitle.textColor = ThemeService.shared.theme.textPrimaryColor; + self.roomTitle1.textColor = ThemeService.shared.theme.textPrimaryColor; + self.roomTitle2.textColor = ThemeService.shared.theme.textPrimaryColor; + self.missedNotifAndUnreadBadgeLabel.textColor = ThemeService.shared.theme.backgroundColor; // Prepare direct room border - CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(kRiotColorGreen.CGColor, kDirectRoomBorderColorAlpha); + CGColorRef directRoomBorderColor = CGColorCreateCopyWithAlpha(ThemeService.shared.theme.tintColor.CGColor, kDirectRoomBorderColorAlpha); [self.directRoomBorderView.layer setCornerRadius:self.directRoomBorderView.frame.size.width / 2]; self.directRoomBorderView.clipsToBounds = YES; @@ -80,7 +81,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; CFRelease(directRoomBorderColor); - self.editionArrowView.backgroundColor = kRiotSecondaryBgColor; + self.editionArrowView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; self.roomAvatar.defaultBackgroundColor = [UIColor clearColor]; } @@ -125,7 +126,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; if (0 < roomCellData.notificationCount) { self.missedNotifAndUnreadBadgeBgView.hidden = NO; - self.missedNotifAndUnreadBadgeBgView.backgroundColor = roomCellData.highlightCount ? kRiotColorPinkRed : kRiotColorGreen; + self.missedNotifAndUnreadBadgeBgView.backgroundColor = roomCellData.highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor; self.missedNotifAndUnreadBadgeLabel.text = roomCellData.notificationCountStringValue; [self.missedNotifAndUnreadBadgeLabel sizeToFit]; @@ -146,7 +147,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0; else if (roomCellData.roomSummary.room.summary.membership == MXMembershipInvite) { self.missedNotifAndUnreadBadgeBgView.hidden = NO; - self.missedNotifAndUnreadBadgeBgView.backgroundColor = kRiotColorPinkRed; + self.missedNotifAndUnreadBadgeBgView.backgroundColor = ThemeService.shared.theme.noticeColor; self.missedNotifAndUnreadBadgeLabel.text = @"!"; [self.missedNotifAndUnreadBadgeLabel sizeToFit]; diff --git a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m index f884672b5..bb7afbb8c 100644 --- a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m +++ b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m @@ -15,7 +15,8 @@ */ #import "TableViewCellWithCollectionView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation TableViewCellWithCollectionView @@ -31,7 +32,7 @@ { [super customizeTableViewCellRendering]; - self.editionView.backgroundColor = kRiotSecondaryBgColor; + self.editionView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; } - (void)prepareForReuse diff --git a/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.swift b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.swift new file mode 100644 index 000000000..c2945d375 --- /dev/null +++ b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.swift @@ -0,0 +1,101 @@ +/* + 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 + +@objc protocol KeyBackupBannerCellDelegate: class { + func keyBackupBannerCellDidTapCloseAction(_ cell: KeyBackupBannerCell) +} + +@objcMembers +final class KeyBackupBannerCell: MXKTableViewCell { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var shieldImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var subtitleLabel: UILabel! + @IBOutlet private weak var closeButton: UIButton! + + // MARK: Public + + weak var delegate: KeyBackupBannerCellDelegate? + + // MARK: - Overrides + + override class func defaultReuseIdentifier() -> String { + return String(describing: self) + } + + override class func nib() -> UINib { + return UINib(nibName: String(describing: self), bundle: nil) + } + + override func customizeRendering() { + super.customizeRendering() + + let theme = ThemeService.shared().theme + + self.shieldImageView.tintColor = theme.textPrimaryColor + self.closeButton.tintColor = theme.textPrimaryColor + + self.titleLabel.textColor = theme.textPrimaryColor + self.subtitleLabel.textColor = theme.tintColor + } + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + + let shieldImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.shieldImageView.image = shieldImage + + let closeImage = Asset.Images.closeBanner.image.withRenderingMode(.alwaysTemplate) + self.closeButton.setImage(closeImage, for: .normal) + } + + // MARK: - Public + + func configure(for banner: KeyBackupBanner) { + + let title: String? + let subtitle: String? + + switch banner { + case .setup: + title = VectorL10n.keyBackupSetupBannerTitle + subtitle = VectorL10n.keyBackupSetupBannerSubtitle + case .recover: + title = VectorL10n.keyBackupRecoverBannerTitle + subtitle = VectorL10n.keyBackupRecoverBannerSubtitle + case .none: + title = nil + subtitle = nil + } + + self.titleLabel.text = title + self.subtitleLabel.text = subtitle + } + + // MARK: - Actions + + @IBAction private func closeButtonAction(_ sender: Any) { + self.delegate?.keyBackupBannerCellDidTapCloseAction(self) + } +} diff --git a/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.xib b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.xib new file mode 100644 index 000000000..6e933fc4d --- /dev/null +++ b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerCell.xib @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Banners/KeyBackupBannerPreferences.swift b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerPreferences.swift new file mode 100644 index 000000000..eb4a2b06b --- /dev/null +++ b/Riot/Modules/KeyBackup/Banners/KeyBackupBannerPreferences.swift @@ -0,0 +1,77 @@ +/* + Copyright 2018 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 + +/// Key backup banner user preferences. +@objcMembers +final class KeyBackupBannerPreferences: NSObject { + + // MARK: - Constants + + private enum UserDefaultsKeys { + static let hideSetupBanner = "KeyBackupBannerPreferencesHideSetupBanner" + static let hiddenRecoverBannerKeyBackupVersions = "KeyBackupBannerPreferencesHiddenRecoverBannerKeyBackupVersions" + } + + static let shared = KeyBackupBannerPreferences() + + // MARK: - Properties + private var hiddenRecoverBannerKeyBackupVersions: [String] { + get { + return UserDefaults.standard.stringArray(forKey: UserDefaultsKeys.hiddenRecoverBannerKeyBackupVersions) ?? [] + } + set { + UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.hiddenRecoverBannerKeyBackupVersions) + } + } + + // MARK: - Public + + /// Remember to hide key backup setup banner. + var hideSetupBanner: Bool { + get { + return UserDefaults.standard.bool(forKey: UserDefaultsKeys.hideSetupBanner) + } + set { + UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.hideSetupBanner) + } + } + + /// Remember to hide key backup recover banner for specific key backup version. + /// + /// - Parameter keyBackupVersion: Key backup version recover banner to hide. + func hideRecoverBanner(for keyBackupVersion: String) { + guard self.hiddenRecoverBannerKeyBackupVersions.contains(keyBackupVersion) == false else { + return + } + self.hiddenRecoverBannerKeyBackupVersions.append(keyBackupVersion) + } + + /// Check if key backup recover banner should be hidden for key backup version. + /// + /// - Parameter keyBackupVersion: Key backup version to check. + /// - Returns: true if recover banner should be hidden. + func isRecoverBannerHidden(for keyBackupVersion: String) -> Bool { + return self.hiddenRecoverBannerKeyBackupVersions.contains(keyBackupVersion) + } + + /// Reset key backup banner preferences to default values + func reset() { + self.hideSetupBanner = false + self.hiddenRecoverBannerKeyBackupVersions = [] + } +} diff --git a/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift b/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift new file mode 100644 index 000000000..1020b0564 --- /dev/null +++ b/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift @@ -0,0 +1,147 @@ +/* + 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 EncryptionKeysExportPresenter: NSObject { + + // MARK: - Constants + + private enum Constants { + static let keyExportFileName = "riot-keys.txt" + } + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let activityViewPresenter: ActivityIndicatorPresenterType + private let keyExportFileURL: URL + + private weak var presentingViewController: UIViewController? + private weak var sourceView: UIView? + private var encryptionKeysExportView: MXKEncryptionKeysExportView? + private var documentInteractionController: UIDocumentInteractionController? + + // MARK: Public + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + self.activityViewPresenter = ActivityIndicatorPresenter() + self.keyExportFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(Constants.keyExportFileName) + super.init() + } + + deinit { + self.deleteKeyExportFile() + } + + // MARK: - Public + + func present(from viewController: UIViewController, sourceView: UIView?) { + self.presentingViewController = viewController + self.sourceView = sourceView + + let keysExportView: MXKEncryptionKeysExportView = MXKEncryptionKeysExportView(matrixSession: self.session) + + // Make sure the file is empty + self.deleteKeyExportFile() + + keysExportView.show(in: viewController, + toExportKeysToFile: self.keyExportFileURL, + onLoading: { [weak self] (loading) in + + guard let sself = self else { + return + } + + if loading { + sself.activityViewPresenter.removeCurrentActivityIndicator(animated: false) + sself.activityViewPresenter.presentActivityIndicator(on: viewController.view, animated: true) + } else { + sself.activityViewPresenter.removeCurrentActivityIndicator(animated: true) + } + }, onComplete: { [weak self] (success) in + guard let sself = self else { + return + } + + guard success else { + sself.encryptionKeysExportView = nil + return + } + + sself.presentInteractionDocumentController() + }) + + self.encryptionKeysExportView = keysExportView + } + + // MARK: - Private + + private func presentInteractionDocumentController() { + + let sourceRect: CGRect + + guard let presentingView = self.presentingViewController?.view else { + self.encryptionKeysExportView = nil + return + } + + if let sourceView = self.sourceView { + sourceRect = sourceView.convert(sourceView.bounds, to: presentingView) + } else { + sourceRect = presentingView.bounds + } + + let documentInteractionController = UIDocumentInteractionController(url: self.keyExportFileURL) + documentInteractionController.delegate = self + + if documentInteractionController.presentOptionsMenu(from: sourceRect, in: presentingView, animated: true) { + self.documentInteractionController = documentInteractionController + } else { + self.encryptionKeysExportView = nil + self.deleteKeyExportFile() + } + } + + @objc private func deleteKeyExportFile() { + + let fileManager = FileManager.default + + if fileManager.fileExists(atPath: self.keyExportFileURL.path) { + try? fileManager.removeItem(atPath: self.keyExportFileURL.path) + } + } +} + +// MARK: - UIDocumentInteractionControllerDelegate +extension EncryptionKeysExportPresenter: UIDocumentInteractionControllerDelegate { + + // Note: This method is not called in all cases (see http://stackoverflow.com/a/21867096). + func documentInteractionController(_ controller: UIDocumentInteractionController, didEndSendingToApplication application: String?) { + self.deleteKeyExportFile() + self.documentInteractionController = nil + } + + func documentInteractionControllerDidDismissOptionsMenu(_ controller: UIDocumentInteractionController) { + self.encryptionKeysExportView = nil + self.documentInteractionController = nil + } +} diff --git a/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinator.swift b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinator.swift new file mode 100644 index 000000000..084bdc25e --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinator.swift @@ -0,0 +1,132 @@ +/* + 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 KeyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let navigationRouter: NavigationRouterType + private let keyBackupVersion: MXKeyBackupVersion + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyBackupRecoverCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, keyBackupVersion: MXKeyBackupVersion) { + self.session = session + self.keyBackupVersion = keyBackupVersion + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + } + + // MARK: - Public + + func start() { + + let rootCoordinator: Coordinator & Presentable + + // Check if a passphrase has been set for given backup + if let megolmBackupAuthData = MXMegolmBackupAuthData(fromJSON: self.keyBackupVersion.authData), megolmBackupAuthData.privateKeySalt != nil { + rootCoordinator = self.createRecoverFromPassphraseCoordinator() + } else { + rootCoordinator = self.createRecoverFromRecoveryKeyCoordinator() + } + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + self.navigationRouter.setRootModule(rootCoordinator) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private + + private func createRecoverFromPassphraseCoordinator() -> KeyBackupRecoverFromPassphraseCoordinator { + let coordinator = KeyBackupRecoverFromPassphraseCoordinator(keyBackup: self.session.crypto.backup, keyBackupVersion: self.keyBackupVersion) + coordinator.delegate = self + return coordinator + } + + private func createRecoverFromRecoveryKeyCoordinator() -> KeyBackupRecoverFromRecoveryKeyCoordinator { + let coordinator = KeyBackupRecoverFromRecoveryKeyCoordinator(keyBackup: self.session.crypto.backup, keyBackupVersion: self.keyBackupVersion) + coordinator.delegate = self + return coordinator + } + + private func showRecoverFromRecoveryKey() { + let coordinator = self.createRecoverFromRecoveryKeyCoordinator() + + self.add(childCoordinator: coordinator) + + self.navigationRouter.push(coordinator, animated: true) { + self.remove(childCoordinator: coordinator) + } + + coordinator.start() + } + + private func showRecoverSuccess() { + let keyBackupRecoverSuccessViewController = KeyBackupRecoverSuccessViewController.instantiate() + keyBackupRecoverSuccessViewController.delegate = self + self.navigationRouter.push(keyBackupRecoverSuccessViewController, animated: true, popCompletion: nil) + } +} + +// MARK: - KeyBackupRecoverFromPassphraseCoordinatorDelegate +extension KeyBackupRecoverCoordinator: KeyBackupRecoverFromPassphraseCoordinatorDelegate { + func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) { + self.showRecoverSuccess() + } + + func keyBackupRecoverFromPassphraseCoordinatorDoNotKnowPassphrase(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) { + self.showRecoverFromRecoveryKey() + } + + func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) { + self.delegate?.keyBackupRecoverCoordinatorDidCancel(self) + } +} + +// MARK: - KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate +extension KeyBackupRecoverCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate { + func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) { + self.showRecoverSuccess() + } + + func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) { + self.delegate?.keyBackupRecoverCoordinatorDidCancel(self) + } +} + +// MARK: - KeyBackupRecoverSuccessViewControllerDelegate +extension KeyBackupRecoverCoordinator: KeyBackupRecoverSuccessViewControllerDelegate { + func KeyBackupRecoverSuccessViewControllerDidTapDone(_ keyBackupRecoverSuccessViewController: KeyBackupRecoverSuccessViewController) { + self.delegate?.keyBackupRecoverCoordinatorDidRecover(self) + } +} + diff --git a/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorBridgePresenter.swift b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..ba3375221 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorBridgePresenter.swift @@ -0,0 +1,79 @@ +/* + 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 KeyBackupRecoverCoordinatorBridgePresenterDelegate { + func keyBackupRecoverCoordinatorBridgePresenterDidCancel(_ keyBackupRecoverCoordinatorBridgePresenter: KeyBackupRecoverCoordinatorBridgePresenter) + func keyBackupRecoverCoordinatorBridgePresenterDidRecover(_ keyBackupRecoverCoordinatorBridgePresenter: KeyBackupRecoverCoordinatorBridgePresenter) +} + +/// KeyBackupRecoverCoordinatorBridgePresenter enables to start KeyBackupRecoverCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class KeyBackupRecoverCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let keyBackupVersion: MXKeyBackupVersion + private var coordinator: KeyBackupRecoverCoordinator? + + // MARK: Public + + weak var delegate: KeyBackupRecoverCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession, keyBackupVersion: MXKeyBackupVersion) { + self.session = session + self.keyBackupVersion = keyBackupVersion + super.init() + } + + // MARK: - Public + + func present(from viewController: UIViewController, animated: Bool) { + let keyBackupSetupCoordinator = KeyBackupRecoverCoordinator(session: self.session, keyBackupVersion: keyBackupVersion) + keyBackupSetupCoordinator.delegate = self + viewController.present(keyBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil) + keyBackupSetupCoordinator.start() + + self.coordinator = keyBackupSetupCoordinator + } + + func dismiss(animated: Bool) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + } + } +} + +// MARK: - KeyBackupRecoverCoordinatorDelegate +extension KeyBackupRecoverCoordinatorBridgePresenter: KeyBackupRecoverCoordinatorDelegate { + func keyBackupRecoverCoordinatorDidRecover(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) { + self.delegate?.keyBackupRecoverCoordinatorBridgePresenterDidRecover(self) + } + + func keyBackupRecoverCoordinatorDidCancel(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) { + self.delegate?.keyBackupRecoverCoordinatorBridgePresenterDidCancel(self) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift new file mode 100644 index 000000000..30d9c34d3 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift @@ -0,0 +1,27 @@ +/* + 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 KeyBackupRecoverCoordinatorDelegate: class { + func keyBackupRecoverCoordinatorDidRecover(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) + func keyBackupRecoverCoordinatorDidCancel(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) +} + +/// `KeyBackupSetupPassphraseCoordinatorType` is a protocol describing a Coordinator that handle key backup recover navigation flow. +protocol KeyBackupRecoverCoordinatorType: Coordinator, Presentable { + var delegate: KeyBackupRecoverCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinator.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinator.swift new file mode 100644 index 000000000..40c85ff7e --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinator.swift @@ -0,0 +1,73 @@ +/* + 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 KeyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let keyBackup: MXKeyBackup + private let keyBackupRecoverFromPassphraseViewController: KeyBackupRecoverFromPassphraseViewController + private let keyBackupVersion: MXKeyBackupVersion + private var keyBackupRecoverFromPassphraseViewModel: KeyBackupRecoverFromPassphraseViewModelType + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyBackupRecoverFromPassphraseCoordinatorDelegate? + + // MARK: - Setup + + init(keyBackup: MXKeyBackup, keyBackupVersion: MXKeyBackupVersion) { + self.keyBackup = keyBackup + self.keyBackupVersion = keyBackupVersion + + let keyBackupRecoverFromPassphraseViewModel = KeyBackupRecoverFromPassphraseViewModel(keyBackup: keyBackup, keyBackupVersion: keyBackupVersion) + let keyBackupRecoverFromPassphraseViewController = KeyBackupRecoverFromPassphraseViewController.instantiate(with: keyBackupRecoverFromPassphraseViewModel) + self.keyBackupRecoverFromPassphraseViewController = keyBackupRecoverFromPassphraseViewController + self.keyBackupRecoverFromPassphraseViewModel = keyBackupRecoverFromPassphraseViewModel + } + + // MARK: - Public + + func start() { + self.keyBackupRecoverFromPassphraseViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.keyBackupRecoverFromPassphraseViewController + } +} + +// MARK: - KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate +extension KeyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate { + func keyBackupRecoverFromPassphraseViewModelDoNotKnowPassphrase(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) { + self.delegate?.keyBackupRecoverFromPassphraseCoordinatorDoNotKnowPassphrase(self) + } + + func keyBackupRecoverFromPassphraseViewModelDidRecover(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) { + self.delegate?.keyBackupRecoverFromPassphraseCoordinatorDidRecover(self) + } + + func keyBackupRecoverFromPassphraseViewModelDidCancel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) { + self.delegate?.keyBackupRecoverFromPassphraseCoordinatorDidCancel(self) + } +} + diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift new file mode 100644 index 000000000..c33cdcf06 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift @@ -0,0 +1,28 @@ +/* + 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 KeyBackupRecoverFromPassphraseCoordinatorDelegate: class { + func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) + func keyBackupRecoverFromPassphraseCoordinatorDoNotKnowPassphrase(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) + func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) +} + +/// `KeyBackupRecoverFromPassphraseCoordinatorType` is a protocol describing a Coordinator that handle key backup passphrase recover navigation flow. +protocol KeyBackupRecoverFromPassphraseCoordinatorType: Coordinator, Presentable { + var delegate: KeyBackupRecoverFromPassphraseCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewAction.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewAction.swift new file mode 100644 index 000000000..d3cba95f9 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewAction.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// KeyBackupRecoverFromPassphraseViewController view actions exposed to view model +enum KeyBackupRecoverFromPassphraseViewAction { + case recover + case unknownPassphrase + case cancel +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.storyboard b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.storyboard new file mode 100644 index 000000000..e3c1c08e6 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.storyboard @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.swift new file mode 100644 index 000000000..018d0255f --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewController.swift @@ -0,0 +1,232 @@ +/* + 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 KeyBackupRecoverFromPassphraseViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var shieldImageView: UIImageView! + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var passphraseTitleLabel: UILabel! + @IBOutlet private weak var passphraseTextField: UITextField! + @IBOutlet private weak var passphraseTextFieldBackgroundView: UIView! + + @IBOutlet private weak var passphraseVisibilityButton: UIButton! + + @IBOutlet private weak var unknownPassphraseButton: UIButton! + + @IBOutlet private weak var recoverButtonBackgroundView: UIView! + @IBOutlet private weak var recoverButton: UIButton! + + // MARK: Private + + private var viewModel: KeyBackupRecoverFromPassphraseViewModelType! + private var keyboardAvoider: KeyboardAvoider? + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: Public + + // MARK: - Setup + + class func instantiate(with viewModel: KeyBackupRecoverFromPassphraseViewModelType) -> KeyBackupRecoverFromPassphraseViewController { + let viewController = StoryboardScene.KeyBackupRecoverFromPassphraseViewController.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.keyBackupRecoverTitle + self.vc_removeBackTitle() + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.viewModel.process(viewAction: .cancel) + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + let shieldImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.shieldImageView.image = shieldImage + + let visibilityImage = Asset.Images.revealPasswordButton.image.withRenderingMode(.alwaysTemplate) + self.passphraseVisibilityButton.setImage(visibilityImage, for: .normal) + + self.informationLabel.text = VectorL10n.keyBackupRecoverFromPassphraseInfo + + self.passphraseTitleLabel.text = VectorL10n.keyBackupRecoverFromPassphrasePassphraseTitle + self.passphraseTextField.addTarget(self, action: #selector(passphraseTextFieldDidChange(_:)), for: .editingChanged) + + self.unknownPassphraseButton.vc_enableMultiLinesTitle() + + self.recoverButton.vc_enableMultiLinesTitle() + self.recoverButton.setTitle(VectorL10n.keyBackupRecoverFromPassphraseRecoverAction, for: .normal) + + self.updateRecoverButton() + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.informationLabel.textColor = theme.textPrimaryColor + + self.shieldImageView.tintColor = theme.textPrimaryColor + + self.passphraseTextFieldBackgroundView.backgroundColor = theme.backgroundColor + self.passphraseTitleLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onTextField: self.passphraseTextField) + self.passphraseTextField.attributedPlaceholder = NSAttributedString(string: VectorL10n.keyBackupRecoverFromPassphrasePassphrasePlaceholder, + attributes: [.foregroundColor : theme.placeholderTextColor]) + + self.theme.applyStyle(onButton: self.passphraseVisibilityButton) + + self.recoverButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.recoverButton) + + let unknownRecoveryKeyAttributedString = NSMutableAttributedString(string: VectorL10n.keyBackupRecoverFromPassphraseLostPassphraseActionPart1, attributes: [.foregroundColor : self.theme.textPrimaryColor]) + let unknownRecoveryKeyAttributedStringPart2 = NSAttributedString(string: VectorL10n.keyBackupRecoverFromPassphraseLostPassphraseActionPart2, attributes: [.foregroundColor : self.theme.tintColor]) + let unknownRecoveryKeyAttributedStringPart3 = NSAttributedString(string: VectorL10n.keyBackupRecoverFromPassphraseLostPassphraseActionPart3, attributes: [.foregroundColor : self.theme.textPrimaryColor]) + + unknownRecoveryKeyAttributedString.append(unknownRecoveryKeyAttributedStringPart2) + unknownRecoveryKeyAttributedString.append(unknownRecoveryKeyAttributedStringPart3) + + self.unknownPassphraseButton.setAttributedTitle(unknownRecoveryKeyAttributedString, for: .normal) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func updateRecoverButton() { + self.recoverButton.isEnabled = self.viewModel.isFormValid + } + + private func render(viewState: KeyBackupRecoverFromPassphraseViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.view.endEditing(true) + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + if (error as NSError).domain == MXKeyBackupErrorDomain + && (error as NSError).code == Int(MXKeyBackupErrorInvalidRecoveryKeyCode.rawValue) { + + self.errorPresenter.presentError(from: self, + title: VectorL10n.keyBackupRecoverInvalidPassphraseTitle, + message: VectorL10n.keyBackupRecoverInvalidPassphrase, + animated: true, + handler: nil) + } else { + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + } + + // MARK: - Actions + + @IBAction private func passphraseVisibilityButtonAction(_ sender: Any) { + self.passphraseTextField.isSecureTextEntry = !self.passphraseTextField.isSecureTextEntry + } + + @objc private func passphraseTextFieldDidChange(_ textField: UITextField) { + self.viewModel.passphrase = textField.text + self.updateRecoverButton() + } + + @IBAction private func recoverButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .recover) + } + + @IBAction private func unknownPassphraseButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .unknownPassphrase) + } +} + +// MARK: - UITextFieldDelegate +extension KeyBackupRecoverFromPassphraseViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } +} + +// MARK: - KeyBackupRecoverFromPassphraseViewModelViewDelegate +extension KeyBackupRecoverFromPassphraseViewController: KeyBackupRecoverFromPassphraseViewModelViewDelegate { + func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromPassphraseViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModel.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModel.swift new file mode 100644 index 000000000..264db3063 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModel.swift @@ -0,0 +1,99 @@ +/* + 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 KeyBackupRecoverFromPassphraseViewModel: KeyBackupRecoverFromPassphraseViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let keyBackup: MXKeyBackup + private var currentHTTPOperation: MXHTTPOperation? + private let keyBackupVersion: MXKeyBackupVersion + + // MARK: Public + + var passphrase: String? + + var isFormValid: Bool { + return self.passphrase?.isEmpty == false + } + + weak var viewDelegate: KeyBackupRecoverFromPassphraseViewModelViewDelegate? + weak var coordinatorDelegate: KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(keyBackup: MXKeyBackup, keyBackupVersion: MXKeyBackupVersion) { + self.keyBackup = keyBackup + self.keyBackupVersion = keyBackupVersion + } + + deinit { + self.currentHTTPOperation?.cancel() + } + + // MARK: - Public + + func process(viewAction: KeyBackupRecoverFromPassphraseViewAction) { + switch viewAction { + case .recover: + self.recoverWithPassphrase() + case .cancel: + self.coordinatorDelegate?.keyBackupRecoverFromPassphraseViewModelDidCancel(self) + case .unknownPassphrase: + self.coordinatorDelegate?.keyBackupRecoverFromPassphraseViewModelDoNotKnowPassphrase(self) + } + } + + // MARK: - Private + + private func recoverWithPassphrase() { + guard let passphrase = self.passphrase else { + return + } + + self.update(viewState: .loading) + + self.currentHTTPOperation = self.keyBackup.restore(self.keyBackupVersion, withPassword: passphrase, room: nil, session: nil, success: { [weak self] (_, _) in + guard let sself = self else { + return + } + + // Trust on decrypt + sself.currentHTTPOperation = sself.keyBackup.trust(sself.keyBackupVersion, trust: true, success: { [weak sself] () in + guard let ssself = sself else { + return + } + + ssself.update(viewState: .loaded) + ssself.coordinatorDelegate?.keyBackupRecoverFromPassphraseViewModelDidRecover(ssself) + + }, failure: { [weak sself] error in + sself?.update(viewState: .error(error)) + }) + + }, failure: { [weak self] error in + self?.update(viewState: .error(error)) + }) + } + + private func update(viewState: KeyBackupRecoverFromPassphraseViewState) { + self.viewDelegate?.keyBackupRecoverFromPassphraseViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift new file mode 100644 index 000000000..8b19bc589 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift @@ -0,0 +1,39 @@ +/* + 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 KeyBackupRecoverFromPassphraseViewModelViewDelegate: class { + func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromPassphraseViewState) +} + +protocol KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate: class { + func keyBackupRecoverFromPassphraseViewModelDidRecover(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) + func keyBackupRecoverFromPassphraseViewModelDidCancel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) + func keyBackupRecoverFromPassphraseViewModelDoNotKnowPassphrase(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) +} + +/// Protocol describing the view model used by `KeyBackupRecoverFromPassphraseViewController` +protocol KeyBackupRecoverFromPassphraseViewModelType { + + var passphrase: String? { get set } + var isFormValid: Bool { get } + + var viewDelegate: KeyBackupRecoverFromPassphraseViewModelViewDelegate? { get set } + var coordinatorDelegate: KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate? { get set } + + func process(viewAction: KeyBackupRecoverFromPassphraseViewAction) +} diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewState.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewState.swift new file mode 100644 index 000000000..c470c0320 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewState.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// KeyBackupRecoverFromPassphraseViewController view state +enum KeyBackupRecoverFromPassphraseViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinator.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinator.swift new file mode 100644 index 000000000..f18f40f60 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinator.swift @@ -0,0 +1,68 @@ +/* + 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 KeyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let keyBackup: MXKeyBackup + private let keyBackupRecoverFromRecoveryKeyViewController: KeyBackupRecoverFromRecoveryKeyViewController + private let keyBackupVersion: MXKeyBackupVersion + private let keyBackupRecoverFromRecoveryKeyViewModel: KeyBackupRecoverFromRecoveryKeyViewModel + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate? + + // MARK: - Setup + + init(keyBackup: MXKeyBackup, keyBackupVersion: MXKeyBackupVersion) { + self.keyBackup = keyBackup + self.keyBackupVersion = keyBackupVersion + + let keyBackupRecoverFromRecoveryKeyViewModel = KeyBackupRecoverFromRecoveryKeyViewModel(keyBackup: keyBackup, keyBackupVersion: keyBackupVersion) + let keyBackupRecoverFromRecoveryKeyViewController = KeyBackupRecoverFromRecoveryKeyViewController.instantiate(with: keyBackupRecoverFromRecoveryKeyViewModel) + self.keyBackupRecoverFromRecoveryKeyViewController = keyBackupRecoverFromRecoveryKeyViewController + self.keyBackupRecoverFromRecoveryKeyViewModel = keyBackupRecoverFromRecoveryKeyViewModel + } + + // MARK: - Public + + func start() { + self.keyBackupRecoverFromRecoveryKeyViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.keyBackupRecoverFromRecoveryKeyViewController + } +} + +// MARK: - KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate +extension KeyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate { + func keyBackupRecoverFromRecoveryKeyViewModelDidRecover(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) { + self.delegate?.keyBackupRecoverFromPassphraseCoordinatorDidRecover(self) + } + + func keyBackupRecoverFromRecoveryKeyViewModelDidCancel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) { + self.delegate?.keyBackupRecoverFromPassphraseCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift new file mode 100644 index 000000000..11d528b4f --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift @@ -0,0 +1,27 @@ +/* + 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 KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate: class { + func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) + func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) +} + +/// `KeyBackupRecoverFromRecoveryKeyCoordinatorType` is a protocol describing a Coordinator that handle key backup recover from recovery key navigation flow. +protocol KeyBackupRecoverFromRecoveryKeyCoordinatorType: Coordinator, Presentable { + var delegate: KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewAction.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewAction.swift new file mode 100644 index 000000000..c1d030e0c --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewAction.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// KeyBackupRecoverFromRecoveryKeyViewController view actions exposed to view model +enum KeyBackupRecoverFromRecoveryKeyViewAction { + case recover + case unknownRecoveryKey + case cancel +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.storyboard b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.storyboard new file mode 100644 index 000000000..84c78334d --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.storyboard @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.swift new file mode 100644 index 000000000..ac097bc27 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewController.swift @@ -0,0 +1,278 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import MobileCoreServices + +final class KeyBackupRecoverFromRecoveryKeyViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var shieldImageView: UIImageView! + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var recoveryKeyTitleLabel: UILabel! + @IBOutlet private weak var recoveryKeyTextField: UITextField! + @IBOutlet private weak var recoveryKeyTextFieldBackgroundView: UIView! + + @IBOutlet private weak var importFileButton: UIButton! + + @IBOutlet private weak var unknownRecoveryKeyButton: UIButton! + + @IBOutlet private weak var recoverButtonBackgroundView: UIView! + @IBOutlet private weak var recoverButton: UIButton! + + // MARK: Private + + private var viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType! + private var keyboardAvoider: KeyboardAvoider? + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + private weak var skipAlertController: UIAlertController? + + // MARK: Public + + // MARK: - Setup + + class func instantiate(with viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) -> KeyBackupRecoverFromRecoveryKeyViewController { + let viewController = StoryboardScene.KeyBackupRecoverFromRecoveryKeyViewController.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.keyBackupRecoverTitle + self.vc_removeBackTitle() + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + let shieldImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.shieldImageView.image = shieldImage + + self.informationLabel.text = VectorL10n.keyBackupRecoverFromRecoveryKeyInfo + + self.recoveryKeyTitleLabel.text = VectorL10n.keyBackupRecoverFromRecoveryKeyRecoveryKeyTitle + self.recoveryKeyTextField.addTarget(self, action: #selector(recoveryKeyTextFieldDidChange(_:)), for: .editingChanged) + + let importFileImage = Asset.Images.importFilesButton.image.withRenderingMode(.alwaysTemplate) + self.importFileButton.setImage(importFileImage, for: .normal) + + self.unknownRecoveryKeyButton.vc_enableMultiLinesTitle() + self.unknownRecoveryKeyButton.setTitle(VectorL10n.keyBackupRecoverFromRecoveryKeyLostRecoveryKeyAction, for: .normal) + // Interaction is disabled for the moment + self.unknownRecoveryKeyButton.isUserInteractionEnabled = false + + self.recoverButton.vc_enableMultiLinesTitle() + self.recoverButton.setTitle(VectorL10n.keyBackupRecoverFromPassphraseRecoverAction, for: .normal) + + self.updateRecoverButton() + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.informationLabel.textColor = theme.textPrimaryColor + + self.shieldImageView.tintColor = theme.textPrimaryColor + + self.recoveryKeyTextFieldBackgroundView.backgroundColor = theme.backgroundColor + self.recoveryKeyTitleLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onTextField: self.recoveryKeyTextField) + self.recoveryKeyTextField.attributedPlaceholder = NSAttributedString(string: VectorL10n.keyBackupRecoverFromRecoveryKeyRecoveryKeyPlaceholder, + attributes: [.foregroundColor : theme.placeholderTextColor]) + + theme.applyStyle(onButton: self.importFileButton) + + self.recoverButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.recoverButton) + + self.unknownRecoveryKeyButton.setTitleColor(theme.textPrimaryColor, for: .normal) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func updateRecoverButton() { + self.recoverButton.isEnabled = self.viewModel.isFormValid + } + + private func render(viewState: KeyBackupRecoverFromRecoveryKeyViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.view.endEditing(true) + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + if (error as NSError).domain == MXKeyBackupErrorDomain + && (error as NSError).code == Int(MXKeyBackupErrorInvalidRecoveryKeyCode.rawValue) { + + self.errorPresenter.presentError(from: self, + title: VectorL10n.keyBackupRecoverInvalidRecoveryKeyTitle, + message: VectorL10n.keyBackupRecoverInvalidRecoveryKey, + animated: true, + handler: nil) + } else { + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + } + + private func showFileSelection() { + // Show only text documents + let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeText as String], in: .import) + documentPicker.delegate = self + self.present(documentPicker, animated: true, completion: nil) + } + + private func importRecoveryKey(from url: URL) { + if let recoveryKey = self.getDocumentContent(from: url) { + self.recoveryKeyTextField.text = recoveryKey + self.recoveryKeyTextFieldDidChange(self.recoveryKeyTextField) + } else { + self.errorPresenter.presentGenericError(from: self, animated: true, handler: nil) + } + } + + private func getDocumentContent(from documentURL: URL) -> String? { + let documentContent: String? + + do { + documentContent = try String(contentsOf: documentURL) + } catch { + print("Error: \(error)") + documentContent = nil + } + + return documentContent + } + + // MARK: - Actions + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } + + @IBAction private func importFileButtonAction(_ sender: Any) { + self.showFileSelection() + } + + @objc private func recoveryKeyTextFieldDidChange(_ textField: UITextField) { + self.viewModel.recoveryKey = textField.text + self.updateRecoverButton() + } + + @IBAction private func usePassphraseButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .recover) + } + + @IBAction private func unknownPassphraseButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .unknownRecoveryKey) + } +} + +// MARK: - UITextFieldDelegate +extension KeyBackupRecoverFromRecoveryKeyViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } +} + +// MARK: - KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate +extension KeyBackupRecoverFromRecoveryKeyViewController: KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate { + func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromRecoveryKeyViewState) { + self.render(viewState: viewSate) + } +} + +// MARK: - UIDocumentPickerDelegate +extension KeyBackupRecoverFromRecoveryKeyViewController: UIDocumentPickerDelegate { + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + guard let documentUrl = urls.first else { + return + } + self.importRecoveryKey(from: documentUrl) + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { + self.importRecoveryKey(from: url) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModel.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModel.swift new file mode 100644 index 000000000..4ad042002 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModel.swift @@ -0,0 +1,99 @@ +/* + 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 KeyBackupRecoverFromRecoveryKeyViewModel: KeyBackupRecoverFromRecoveryKeyViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let keyBackup: MXKeyBackup + private var currentHTTPOperation: MXHTTPOperation? + private let keyBackupVersion: MXKeyBackupVersion + + // MARK: Public + + var recoveryKey: String? + + var isFormValid: Bool { + return self.recoveryKey?.isEmpty == false + } + + weak var viewDelegate: KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate? + weak var coordinatorDelegate: KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(keyBackup: MXKeyBackup, keyBackupVersion: MXKeyBackupVersion) { + self.keyBackup = keyBackup + self.keyBackupVersion = keyBackupVersion + } + + deinit { + self.currentHTTPOperation?.cancel() + } + + // MARK: - Public + + func process(viewAction: KeyBackupRecoverFromRecoveryKeyViewAction) { + switch viewAction { + case .recover: + self.recover() + case .cancel: + self.coordinatorDelegate?.keyBackupRecoverFromRecoveryKeyViewModelDidCancel(self) + case .unknownRecoveryKey: + break + } + } + + // MARK: - Private + + private func recover() { + guard let recoveryKey = self.recoveryKey else { + return + } + + self.update(viewState: .loading) + + self.currentHTTPOperation = self.keyBackup.restore(self.keyBackupVersion, withRecoveryKey: recoveryKey, room: nil, session: nil, success: { [weak self] (_, _) in + guard let sself = self else { + return + } + + // Trust on decrypt + sself.currentHTTPOperation = sself.keyBackup.trust(sself.keyBackupVersion, trust: true, success: { [weak sself] () in + guard let ssself = sself else { + return + } + + ssself.update(viewState: .loaded) + ssself.coordinatorDelegate?.keyBackupRecoverFromRecoveryKeyViewModelDidRecover(ssself) + + }, failure: {[weak sself] error in + sself?.update(viewState: .error(error)) + }) + + }, failure: {[weak self] error in + self?.update(viewState: .error(error)) + }) + } + + private func update(viewState: KeyBackupRecoverFromRecoveryKeyViewState) { + self.viewDelegate?.keyBackupRecoverFromPassphraseViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift new file mode 100644 index 000000000..e952c70a3 --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift @@ -0,0 +1,38 @@ +/* + 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 KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate: class { + func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromRecoveryKeyViewState) +} + +protocol KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate: class { + func keyBackupRecoverFromRecoveryKeyViewModelDidRecover(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) + func keyBackupRecoverFromRecoveryKeyViewModelDidCancel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) +} + +/// Protocol describing the view model used by `KeyBackupSetupPassphraseViewController` +protocol KeyBackupRecoverFromRecoveryKeyViewModelType { + + var recoveryKey: String? { get set } + var isFormValid: Bool { get } + + var viewDelegate: KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate? { get set } + var coordinatorDelegate: KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate? { get set } + + func process(viewAction: KeyBackupRecoverFromRecoveryKeyViewAction) +} diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewState.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewState.swift new file mode 100644 index 000000000..e772a887f --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewState.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// KeyBackupRecoverFromRecoveryKeyViewController view state +enum KeyBackupRecoverFromRecoveryKeyViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.storyboard b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.storyboard new file mode 100644 index 000000000..8e2e08f6d --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.storyboard @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift new file mode 100644 index 000000000..8d126897d --- /dev/null +++ b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift @@ -0,0 +1,120 @@ +/* + 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 + +protocol KeyBackupRecoverSuccessViewControllerDelegate: class { + func KeyBackupRecoverSuccessViewControllerDidTapDone(_ keyBackupRecoverSuccessViewController: KeyBackupRecoverSuccessViewController) +} + +final class KeyBackupRecoverSuccessViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var shieldImageView: UIImageView! + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var doneButtonBackgroundView: UIView! + @IBOutlet private weak var doneButton: UIButton! + + // MARK: Private + + private var theme: Theme! + + // MARK: Public + + weak var delegate: KeyBackupRecoverSuccessViewControllerDelegate? + + // MARK: - Setup + + class func instantiate() -> KeyBackupRecoverSuccessViewController { + let viewController = StoryboardScene.KeyBackupRecoverSuccessViewController.initialScene.instantiate() + 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.keyBackupRecoverTitle + self.vc_removeBackTitle() + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let shieldImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.shieldImageView.image = shieldImage + + self.informationLabel.text = VectorL10n.keyBackupRecoverSuccessInfo + + self.doneButton.vc_enableMultiLinesTitle() + self.doneButton.setTitle(VectorL10n.keyBackupRecoverDoneAction, for: .normal) + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.shieldImageView.tintColor = theme.textPrimaryColor + + self.informationLabel.textColor = theme.textPrimaryColor + + self.doneButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.doneButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + // MARK: - Actions + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + @IBAction private func doneButtonAction(_ sender: Any) { + self.delegate?.KeyBackupRecoverSuccessViewControllerDidTapDone(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.storyboard b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.storyboard new file mode 100644 index 000000000..ddd5f89c3 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.storyboard @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift new file mode 100644 index 000000000..8963b5af8 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift @@ -0,0 +1,161 @@ +/* + 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 + +protocol KeyBackupSetupIntroViewControllerDelegate: class { + func keyBackupSetupIntroViewControllerDidTapSetupAction(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) + func keyBackupSetupIntroViewControllerDidCancel(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) +} + +final class KeyBackupSetupIntroViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var keyBackupLogoImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var setUpButtonBackgroundView: UIView! + @IBOutlet private weak var setUpButton: UIButton! + + @IBOutlet private weak var manualExportContainerView: UIView! + @IBOutlet private weak var manualExportInfoLabel: UILabel! + @IBOutlet private weak var manualExportButton: UIButton! + + // MARK: Private + + private var theme: Theme! + private var isABackupAlreadyExists: Bool = false + private var encryptionKeysExportPresenter: EncryptionKeysExportPresenter? + + private var showManualExport: Bool { + return self.encryptionKeysExportPresenter != nil + } + + // MARK: Public + + weak var delegate: KeyBackupSetupIntroViewControllerDelegate? + + // MARK: - Setup + + class func instantiate(isABackupAlreadyExists: Bool, encryptionKeysExportPresenter: EncryptionKeysExportPresenter?) -> KeyBackupSetupIntroViewController { + let viewController = StoryboardScene.KeyBackupSetupIntroViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + viewController.isABackupAlreadyExists = isABackupAlreadyExists + viewController.encryptionKeysExportPresenter = encryptionKeysExportPresenter + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = VectorL10n.keyBackupSetupTitle + self.vc_removeBackTitle() + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.showSkipAlert() + } + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + let keybackupLogoImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.keyBackupLogoImageView.image = keybackupLogoImage + + self.titleLabel.text = VectorL10n.keyBackupSetupIntroTitle + self.informationLabel.text = VectorL10n.keyBackupSetupIntroInfo + + let setupTitle = self.isABackupAlreadyExists ? VectorL10n.keyBackupSetupIntroSetupActionWithExistingBackup : VectorL10n.keyBackupSetupIntroSetupActionWithoutExistingBackup + + self.setUpButton.setTitle(setupTitle, for: .normal) + + self.manualExportInfoLabel.text = VectorL10n.keyBackupSetupIntroManualExportInfo + + self.manualExportContainerView.isHidden = !self.showManualExport + self.manualExportButton.setTitle(VectorL10n.keyBackupSetupIntroManualExportAction, for: .normal) + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.keyBackupLogoImageView.tintColor = theme.textPrimaryColor + + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + + self.setUpButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.setUpButton) + + self.manualExportInfoLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onButton: self.manualExportButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + private func showSkipAlert() { + let alertController = UIAlertController(title: VectorL10n.keyBackupSetupSkipAlertTitle, + message: VectorL10n.keyBackupSetupSkipAlertMessage, + preferredStyle:.alert) + + alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in + })) + + alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction , style: .default, handler: { action in + self.delegate?.keyBackupSetupIntroViewControllerDidCancel(self) + })) + + self.present(alertController, animated: true, completion: nil) + } + + // MARK: - Actions + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + @IBAction private func validateButtonAction(_ sender: Any) { + self.delegate?.keyBackupSetupIntroViewControllerDidTapSetupAction(self) + } + + @IBAction private func manualExportButtonAction(_ sender: Any) { + self.encryptionKeysExportPresenter?.present(from: self, sourceView: self.manualExportButton) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift new file mode 100644 index 000000000..e5dd5561b --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift @@ -0,0 +1,148 @@ +/* + 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 KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let session: MXSession + private let isStartedFromSignOut: Bool + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyBackupSetupCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, isStartedFromSignOut: Bool) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.session = session + self.isStartedFromSignOut = isStartedFromSignOut + } + + // MARK: - Public methods + + func start() { + + // Set key backup setup intro as root controller + let keyBackupSetupIntroViewController = self.createSetupIntroViewController() + keyBackupSetupIntroViewController.delegate = self + self.navigationRouter.setRootModule(keyBackupSetupIntroViewController) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createSetupIntroViewController() -> KeyBackupSetupIntroViewController { + + let backupState = self.session.crypto.backup?.state ?? MXKeyBackupStateUnknown + let isABackupAlreadyExists: Bool + + switch backupState { + case MXKeyBackupStateUnknown, MXKeyBackupStateDisabled, MXKeyBackupStateCheckingBackUpOnHomeserver: + isABackupAlreadyExists = false + default: + isABackupAlreadyExists = true + } + + let encryptionKeysExportPresenter: EncryptionKeysExportPresenter? + + if self.isStartedFromSignOut { + encryptionKeysExportPresenter = EncryptionKeysExportPresenter(session: self.session) + } else { + encryptionKeysExportPresenter = nil + } + + return KeyBackupSetupIntroViewController.instantiate(isABackupAlreadyExists: isABackupAlreadyExists, encryptionKeysExportPresenter: encryptionKeysExportPresenter) + } + + private func showSetupPassphrase(animated: Bool) { + let keyBackupSetupPassphraseCoordinator = KeyBackupSetupPassphraseCoordinator(session: self.session) + keyBackupSetupPassphraseCoordinator.delegate = self + keyBackupSetupPassphraseCoordinator.start() + + self.add(childCoordinator: keyBackupSetupPassphraseCoordinator) + self.navigationRouter.push(keyBackupSetupPassphraseCoordinator, animated: animated) { [weak self] in + self?.remove(childCoordinator: keyBackupSetupPassphraseCoordinator) + } + } + + private func showSetupRecoveryKeySuccess(with recoveryKey: String, animated: Bool) { + + let viewController = KeyBackupSetupSuccessFromRecoveryKeyViewController.instantiate(with: recoveryKey) + viewController.delegate = self + self.navigationRouter.push(viewController, animated: animated, popCompletion: nil) + } + + private func showSetupPassphraseSuccess(with recoveryKey: String, animated: Bool) { + + let viewController = KeyBackupSetupSuccessFromPassphraseViewController.instantiate(with: recoveryKey) + viewController.delegate = self + self.navigationRouter.push(viewController, animated: animated, popCompletion: nil) + } +} + +// MARK: - KeyBackupSetupIntroViewControllerDelegate +extension KeyBackupSetupCoordinator: KeyBackupSetupIntroViewControllerDelegate { + + func keyBackupSetupIntroViewControllerDidTapSetupAction(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) { + self.showSetupPassphrase(animated: true) + } + + func keyBackupSetupIntroViewControllerDidCancel(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) { + self.delegate?.keyBackupSetupCoordinatorDidCancel(self) + } +} + +// MARK: - KeyRecoveryPassphraseCoordinatorDelegate +extension KeyBackupSetupCoordinator: KeyBackupSetupPassphraseCoordinatorDelegate { + func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) { + self.showSetupPassphraseSuccess(with: recoveryKey, animated: true) + } + + func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromRecoveryKey recoveryKey: String) { + self.showSetupRecoveryKeySuccess(with: recoveryKey, animated: true) + } + + func keyBackupSetupPassphraseCoordinatorDidCancel(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType) { + self.delegate?.keyBackupSetupCoordinatorDidCancel(self) + } +} + +// MARK: - KeyBackupSetupSuccessFromPassphraseViewControllerDelegate +extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromPassphraseViewControllerDelegate { + func keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromPassphraseViewController) { + self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self) + } +} + +// MARK: - KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate +extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate { + func keyBackupSetupSuccessFromRecoveryKeyViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromRecoveryKeyViewController) { + self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..06c0b7856 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorBridgePresenter.swift @@ -0,0 +1,82 @@ +/* + 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 KeyBackupSetupCoordinatorBridgePresenterDelegate { + func keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(_ keyBackupSetupCoordinatorBridgePresenter: KeyBackupSetupCoordinatorBridgePresenter) + func keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey(_ keyBackupSetupCoordinatorBridgePresenter: KeyBackupSetupCoordinatorBridgePresenter) +} + +/// KeyBackupSetupCoordinatorBridgePresenter enables to start KeyBackupSetupCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class KeyBackupSetupCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var coordinator: KeyBackupSetupCoordinator? + + // MARK: Public + + weak var delegate: KeyBackupSetupCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + 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, isStartedFromSignOut: false, animated: animated) + } + + func present(from viewController: UIViewController, isStartedFromSignOut: Bool, animated: Bool) { + let keyBackupSetupCoordinator = KeyBackupSetupCoordinator(session: self.session, isStartedFromSignOut: isStartedFromSignOut) + keyBackupSetupCoordinator.delegate = self + viewController.present(keyBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil) + keyBackupSetupCoordinator.start() + + self.coordinator = keyBackupSetupCoordinator + } + + func dismiss(animated: Bool) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + } + } +} + +// MARK: - KeyBackupSetupCoordinatorDelegate +extension KeyBackupSetupCoordinatorBridgePresenter: KeyBackupSetupCoordinatorDelegate { + func keyBackupSetupCoordinatorDidCancel(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) { + self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(self) + } + + func keyBackupSetupCoordinatorDidSetupRecoveryKey(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) { + self.delegate?.keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift new file mode 100644 index 000000000..f5a5dd0b0 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift @@ -0,0 +1,27 @@ +/* + 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 KeyBackupSetupCoordinatorDelegate: class { + func keyBackupSetupCoordinatorDidCancel(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) + func keyBackupSetupCoordinatorDidSetupRecoveryKey(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) +} + +/// `KeyBackupSetupCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol KeyBackupSetupCoordinatorType: Coordinator, Presentable { + var delegate: KeyBackupSetupCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift new file mode 100644 index 000000000..ea0d2b549 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinator.swift @@ -0,0 +1,72 @@ +/* + 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 KeyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var keyBackupSetupPassphraseViewModel: KeyBackupSetupPassphraseViewModelType + private let keyBackupSetupPassphraseViewController: KeyBackupSetupPassphraseViewController + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: KeyBackupSetupPassphraseCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + + let keyBackupSetupPassphraseViewModel = KeyBackupSetupPassphraseViewModel(keyBackup: self.session.crypto.backup) + let keyBackupSetupPassphraseViewController = KeyBackupSetupPassphraseViewController.instantiate(with: keyBackupSetupPassphraseViewModel) + self.keyBackupSetupPassphraseViewModel = keyBackupSetupPassphraseViewModel + self.keyBackupSetupPassphraseViewController = keyBackupSetupPassphraseViewController + } + + // MARK: - Public methods + + func start() { + self.keyBackupSetupPassphraseViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.keyBackupSetupPassphraseViewController + } +} + +// MARK: - KeyBackupSetupPassphraseViewModelCoordinatorDelegate +extension KeyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseViewModelCoordinatorDelegate { + + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) { + self.delegate?.keyBackupSetupPassphraseCoordinator(self, didCreateBackupFromPassphraseWithResultingRecoveryKey: recoveryKey) + } + + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromRecoveryKey recoveryKey: String) { + self.delegate?.keyBackupSetupPassphraseCoordinator(self, didCreateBackupFromRecoveryKey: recoveryKey) + } + + func keyBackupSetupPassphraseViewModelDidCancel(_ viewModel: KeyBackupSetupPassphraseViewModelType) { + self.delegate?.keyBackupSetupPassphraseCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift new file mode 100644 index 000000000..f94722adb --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift @@ -0,0 +1,28 @@ +/* + 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 KeyBackupSetupPassphraseCoordinatorDelegate: class { + func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) + func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromRecoveryKey recoveryKey: String) + func keyBackupSetupPassphraseCoordinatorDidCancel(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType) +} + +/// `KeyBackupSetupPassphraseCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol KeyBackupSetupPassphraseCoordinatorType: Coordinator, Presentable { + var delegate: KeyBackupSetupPassphraseCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewAction.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewAction.swift new file mode 100644 index 000000000..b6919a730 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewAction.swift @@ -0,0 +1,26 @@ +/* + 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 + +/// KeyBackupSetupPassphraseViewController view actions exposed to view model +enum KeyBackupSetupPassphraseViewAction { + case setupPassphrase + case setupRecoveryKey + case skip + case skipAlertSkip + case skipAlertContinue +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.storyboard b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.storyboard new file mode 100644 index 000000000..2e7a05579 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.storyboard @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.swift new file mode 100644 index 000000000..c9d51ff90 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewController.swift @@ -0,0 +1,411 @@ +/* + 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 KeyBackupSetupPassphraseViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let animationDuration: TimeInterval = 0.3 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var formBackgroundView: UIView! + + @IBOutlet private weak var passphraseTitleLabel: UILabel! + @IBOutlet private weak var passphraseTextField: UITextField! + + @IBOutlet private weak var passphraseAdditionalInfoView: UIView! + @IBOutlet private weak var passphraseStrengthView: PasswordStrengthView! + @IBOutlet private weak var passphraseAdditionalLabel: UILabel! + + @IBOutlet private weak var formSeparatorView: UIView! + + @IBOutlet private weak var confirmPassphraseTitleLabel: UILabel! + @IBOutlet private weak var confirmPassphraseTextField: UITextField! + + @IBOutlet private weak var confirmPassphraseAdditionalInfoView: UIView! + @IBOutlet private weak var confirmPassphraseAdditionalLabel: UILabel! + + @IBOutlet private weak var setPassphraseButtonBackgroundView: UIView! + @IBOutlet private weak var setPassphraseButton: UIButton! + + @IBOutlet private weak var setUpRecoveryKeyInfoLabel: UILabel! + @IBOutlet private weak var setUpRecoveryKeyButton: UIButton! + + // MARK: Private + + private var isFirstViewAppearing: Bool = true + private var isPassphraseTextFieldEditedOnce: Bool = false + private var isConfirmPassphraseTextFieldEditedOnce: Bool = false + private var keyboardAvoider: KeyboardAvoider? + private var viewModel: KeyBackupSetupPassphraseViewModelType! + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + private weak var skipAlertController: UIAlertController? + + // MARK: - Setup + + class func instantiate(with viewModel: KeyBackupSetupPassphraseViewModelType) -> KeyBackupSetupPassphraseViewController { + let viewController = StoryboardScene.KeyBackupSetupPassphraseViewController.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.keyBackupSetupTitle + self.vc_removeBackTitle() + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if self.isFirstViewAppearing { + self.isFirstViewAppearing = false + } + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.view.endEditing(true) + self.keyboardAvoider?.stopAvoiding() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if self.isFirstViewAppearing { + // Workaround to layout passphraseStrengthView corner radius + self.passphraseStrengthView.setNeedsLayout() + } + } + + 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) + } + + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + + self.formBackgroundView.backgroundColor = theme.backgroundColor + self.passphraseTitleLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onTextField: self.passphraseTextField) + self.passphraseTextField.attributedPlaceholder = NSAttributedString(string: VectorL10n.keyBackupSetupPassphrasePassphrasePlaceholder, + attributes: [.foregroundColor : theme.placeholderTextColor]) + self.updatePassphraseAdditionalLabel() + + self.formSeparatorView.backgroundColor = theme.lineBreakColor + + self.confirmPassphraseTitleLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onTextField: self.confirmPassphraseTextField) + self.confirmPassphraseTextField.attributedPlaceholder = NSAttributedString(string: VectorL10n.keyBackupSetupPassphraseConfirmPassphraseTitle, + attributes: [.foregroundColor : theme.placeholderTextColor]) + self.updateConfirmPassphraseAdditionalLabel() + + self.setPassphraseButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.setPassphraseButton) + + self.setUpRecoveryKeyInfoLabel.textColor = theme.textPrimaryColor + theme.applyStyle(onButton: self.setUpRecoveryKeyButton) + } + + 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.titleLabel.text = VectorL10n.keyBackupSetupPassphraseTitle + self.informationLabel.text = VectorL10n.keyBackupSetupPassphraseInfo + + self.passphraseTitleLabel.text = VectorL10n.keyBackupSetupPassphrasePassphraseTitle + self.passphraseTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) + self.passphraseStrengthView.strength = self.viewModel.passphraseStrength + self.passphraseAdditionalInfoView.isHidden = true + + self.confirmPassphraseTitleLabel.text = VectorL10n.keyBackupSetupPassphraseConfirmPassphraseTitle + self.confirmPassphraseTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) + self.confirmPassphraseAdditionalInfoView.isHidden = true + + self.setPassphraseButton.vc_enableMultiLinesTitle() + self.setPassphraseButton.setTitle(VectorL10n.keyBackupSetupPassphraseSetPassphraseAction, for: .normal) + + self.updateSetPassphraseButton() + } + + private func showPassphraseAdditionalInfo(animated: Bool) { + guard self.passphraseAdditionalInfoView.isHidden else { + return + } + + UIView.animate(withDuration: Constants.animationDuration) { + self.passphraseAdditionalInfoView.isHidden = false + } + } + + private func showConfirmPassphraseAdditionalInfo(animated: Bool) { + guard self.confirmPassphraseAdditionalInfoView.isHidden else { + return + } + + UIView.animate(withDuration: Constants.animationDuration) { + self.confirmPassphraseAdditionalInfoView.isHidden = false + } + } + + private func hideConfirmPassphraseAdditionalInfo(animated: Bool) { + guard self.confirmPassphraseAdditionalInfoView.isHidden == false else { + return + } + + UIView.animate(withDuration: Constants.animationDuration) { + self.confirmPassphraseAdditionalInfoView.isHidden = true + } + } + + private func updatePassphraseStrengthView() { + self.passphraseStrengthView.strength = self.viewModel.passphraseStrength + } + + private func updatePassphraseAdditionalLabel() { + + let text: String + let textColor: UIColor + + if self.viewModel.isPassphraseValid { + text = VectorL10n.keyBackupSetupPassphrasePassphraseValid + textColor = self.theme.tintColor + } else { + text = VectorL10n.keyBackupSetupPassphrasePassphraseInvalid + textColor = self.theme.noticeColor + } + + self.passphraseAdditionalLabel.text = text + self.passphraseAdditionalLabel.textColor = textColor + } + + private func updateConfirmPassphraseAdditionalLabel() { + + let text: String + let textColor: UIColor + + if self.viewModel.isConfirmPassphraseValid { + text = VectorL10n.keyBackupSetupPassphraseConfirmPassphraseValid + textColor = self.theme.tintColor + } else { + text = VectorL10n.keyBackupSetupPassphraseConfirmPassphraseInvalid + textColor = self.theme.noticeColor + } + + self.confirmPassphraseAdditionalLabel.text = text + self.confirmPassphraseAdditionalLabel.textColor = textColor + } + + private func updateSetPassphraseButton() { + self.setPassphraseButton.isEnabled = self.viewModel.isFormValid + } + + private func render(viewState: KeyBackupSetupPassphraseViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.view.endEditing(true) + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.hideSkipAlert(animated: false) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + private func showSkipAlert() { + guard self.skipAlertController == nil else { + return + } + + let alertController = UIAlertController(title: VectorL10n.keyBackupSetupSkipAlertTitle, + message: VectorL10n.keyBackupSetupSkipAlertMessage, + preferredStyle:.alert) + + alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in + self.viewModel.process(viewAction: .skipAlertContinue) + })) + + alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction , style: .default, handler: { action in + self.viewModel.process(viewAction: .skipAlertSkip) + })) + + self.present(alertController, animated: true, completion: nil) + self.skipAlertController = alertController + } + + private func hideSkipAlert(animated: Bool) { + self.skipAlertController?.dismiss(animated: true, completion: nil) + } + + // MARK: - Actions + + @IBAction private func passphraseVisibilityButtonAction(_ sender: Any) { + guard self.isPassphraseTextFieldEditedOnce else { + return + } + self.passphraseTextField.isSecureTextEntry = !self.passphraseTextField.isSecureTextEntry + // TODO: Use this when project will be migrated to Swift 4.2 + // self.passphraseTextField.isSecureTextEntry.toggle() + } + + @objc private func textFieldDidChange(_ textField: UITextField) { + + if textField == self.passphraseTextField { + self.viewModel.passphrase = textField.text + + self.updatePassphraseAdditionalLabel() + self.updatePassphraseStrengthView() + + // Show passphrase additional info at first character entered + if self.isPassphraseTextFieldEditedOnce == false && textField.text?.isEmpty == false { + self.isPassphraseTextFieldEditedOnce = true + self.showPassphraseAdditionalInfo(animated: true) + } + } else { + self.viewModel.confirmPassphrase = textField.text + } + + // Show confirm passphrase additional info if needed + self.updateConfirmPassphraseAdditionalLabel() + if self.viewModel.confirmPassphrase?.isEmpty == false && self.viewModel.isPassphraseValid { + self.showConfirmPassphraseAdditionalInfo(animated: true) + } else { + self.hideConfirmPassphraseAdditionalInfo(animated: true) + } + + // Enable validate button if form is valid + self.updateSetPassphraseButton() + } + + @IBAction private func setPassphraseButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .setupPassphrase) + } + + @IBAction private func setUpRecoveryKeyButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .setupRecoveryKey) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .skip) + } +} + +// MARK: - UITextFieldDelegate +extension KeyBackupSetupPassphraseViewController: UITextFieldDelegate { + + func textFieldShouldClear(_ textField: UITextField) -> Bool { + self.textFieldDidChange(textField) + return true + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + + if textField == self.passphraseTextField { + self.confirmPassphraseTextField.becomeFirstResponder() + } else { + textField.resignFirstResponder() + } + + return true + } +} + +// MARK: - KeyBackupSetupPassphraseViewModelViewDelegate +extension KeyBackupSetupPassphraseViewController: KeyBackupSetupPassphraseViewModelViewDelegate { + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupSetupPassphraseViewState) { + self.render(viewState: viewSate) + } + + func keyBackupSetupPassphraseViewModelShowSkipAlert(_ viewModel: KeyBackupSetupPassphraseViewModelType) { + self.showSkipAlert() + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModel.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModel.swift new file mode 100644 index 000000000..ea2fd4752 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModel.swift @@ -0,0 +1,161 @@ +/* + 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 KeyBackupSetupPassphraseViewModel: KeyBackupSetupPassphraseViewModelType { + + // MARK: - Properties + + // MARK: Private + + private(set) var passphraseStrength: PasswordStrength = .tooGuessable + private let passwordStrengthManager: PasswordStrengthManager + private let keyBackup: MXKeyBackup + private let coordinatorDelegateQueue: OperationQueue + private var createKeyBackupOperation: MXHTTPOperation? + + // MARK: Public + + var passphrase: String? { + didSet { + self.updatePassphraseStrength() + } + } + + var confirmPassphrase: String? + + var isPassphraseValid: Bool { + return self.passphraseStrength == .veryUnguessable + } + + var isConfirmPassphraseValid: Bool { + guard self.isPassphraseValid, let confirmPassphrase = self.confirmPassphrase else { + return false + } + return confirmPassphrase == passphrase + } + + var isFormValid: Bool { + return self.isPassphraseValid && self.isConfirmPassphraseValid + } + + weak var viewDelegate: KeyBackupSetupPassphraseViewModelViewDelegate? + weak var coordinatorDelegate: KeyBackupSetupPassphraseViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(keyBackup: MXKeyBackup) { + self.passwordStrengthManager = PasswordStrengthManager() + self.keyBackup = keyBackup + self.coordinatorDelegateQueue = OperationQueue.vc_createSerialOperationQueue(name: "\(type(of: self)).coordinatorDelegateQueue") + } + + deinit { + self.createKeyBackupOperation?.cancel() + } + + // MARK: - Public + + func process(viewAction: KeyBackupSetupPassphraseViewAction) { + switch viewAction { + case .setupPassphrase: + self.setupPassphrase() + case .setupRecoveryKey: + self.setupRecoveryKey() + case .skip: + self.coordinatorDelegateQueue.vc_pause() + self.viewDelegate?.keyBackupSetupPassphraseViewModelShowSkipAlert(self) + case.skipAlertContinue: + self.coordinatorDelegateQueue.vc_resume() + case.skipAlertSkip: + self.coordinatorDelegateQueue.cancelAllOperations() + self.coordinatorDelegate?.keyBackupSetupPassphraseViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func setupPassphrase() { + guard let passphrase = self.passphrase else { + return + } + + self.update(viewState: .loading) + + self.keyBackup.prepareKeyBackupVersion(withPassword: passphrase, success: { [weak self] (megolmBackupCreationInfo) in + guard let sself = self else { + return + } + + sself.createKeyBackupOperation = sself.keyBackup.createKeyBackupVersion(megolmBackupCreationInfo, success: { (_) in + + sself.update(viewState: .loaded) + + sself.coordinatorDelegateQueue.addOperation { + DispatchQueue.main.async { + sself.coordinatorDelegate?.keyBackupSetupPassphraseViewModel(sself, didCreateBackupFromPassphraseWithResultingRecoveryKey: megolmBackupCreationInfo.recoveryKey) + } + } + + }, failure: { (error) in + self?.update(viewState: .error(error)) + }) + }, failure: { [weak self] error in + self?.update(viewState: .error(error)) + }) + } + + private func setupRecoveryKey() { + self.update(viewState: .loading) + + self.keyBackup.prepareKeyBackupVersion(withPassword: nil, success: { [weak self] (megolmBackupCreationInfo) in + guard let sself = self else { + return + } + + sself.createKeyBackupOperation = sself.keyBackup.createKeyBackupVersion(megolmBackupCreationInfo, success: { (_) in + + sself.update(viewState: .loaded) + + sself.coordinatorDelegateQueue.addOperation { + DispatchQueue.main.async { + sself.coordinatorDelegate?.keyBackupSetupPassphraseViewModel(sself, didCreateBackupFromRecoveryKey: megolmBackupCreationInfo.recoveryKey) + } + } + }, failure: { (error) in + self?.update(viewState: .error(error)) + }) + }, failure: { [weak self] error in + self?.update(viewState: .error(error)) + }) + } + + private func updatePassphraseStrength() { + self.passphraseStrength = self.passwordStrength(for: self.passphrase) + } + + private func passwordStrength(for password: String?) -> PasswordStrength { + guard let password = password else { + return .tooGuessable + } + return self.passwordStrengthManager.passwordStrength(for: password) + } + + private func update(viewState: KeyBackupSetupPassphraseViewState) { + self.viewDelegate?.keyBackupSetupPassphraseViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift new file mode 100644 index 000000000..2f4b2dfaa --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift @@ -0,0 +1,45 @@ +/* + 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 KeyBackupSetupPassphraseViewModelViewDelegate: class { + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupSetupPassphraseViewState) + func keyBackupSetupPassphraseViewModelShowSkipAlert(_ viewModel: KeyBackupSetupPassphraseViewModelType) +} + +protocol KeyBackupSetupPassphraseViewModelCoordinatorDelegate: class { + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) + func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromRecoveryKey recoveryKey: String) + func keyBackupSetupPassphraseViewModelDidCancel(_ viewModel: KeyBackupSetupPassphraseViewModelType) +} + +/// Protocol describing the view model used by `KeyBackupSetupPassphraseViewController` +protocol KeyBackupSetupPassphraseViewModelType { + + var passphrase: String? { get set } + var confirmPassphrase: String? { get set } + var passphraseStrength: PasswordStrength { get } + + var isPassphraseValid: Bool { get } + var isConfirmPassphraseValid: Bool { get } + var isFormValid: Bool { get } + + var viewDelegate: KeyBackupSetupPassphraseViewModelViewDelegate? { get set } + var coordinatorDelegate: KeyBackupSetupPassphraseViewModelCoordinatorDelegate? { get set } + + func process(viewAction: KeyBackupSetupPassphraseViewAction) +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewState.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewState.swift new file mode 100644 index 000000000..ba0e09e1d --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewState.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// KeyBackupSetupPassphraseViewController view state +enum KeyBackupSetupPassphraseViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.swift new file mode 100644 index 000000000..e7931e50e --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.swift @@ -0,0 +1,130 @@ +/* + 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 Reusable + +final class PasswordStrengthView: UIView, NibOwnerLoadable { + + // MARK: - Constants + + private enum StrengthColors { + static let gray = UIColor(rgb: 0x9E9E9E) + static let red = UIColor(rgb: 0xF56679) + static let orange = UIColor(rgb: 0xFFC666) + static let yellow = UIColor(rgb: 0xF8E71C) + static let green = UIColor(rgb: 0x7AC9A1) + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var firstStrengthView: UIView! + @IBOutlet private weak var secondStrengthView: UIView! + @IBOutlet private weak var thirdStrengthView: UIView! + @IBOutlet private weak var fourthStrengthView: UIView! + + // MARK: Private + + private var strengthViews: [UIView] = [] + + // MARK: Public + + var strength: PasswordStrength = .tooGuessable { + didSet { + self.updateStrengthColors() + } + } + + // MARK: - Setup + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + } + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + + self.strengthViews = [self.firstStrengthView, + self.secondStrengthView, + self.thirdStrengthView, + self.fourthStrengthView] + + for strenghView in self.strengthViews { + strenghView.layer.masksToBounds = true + } + } + + override func layoutSubviews() { + super.layoutSubviews() + + for strenghView in self.strengthViews { + strenghView.layer.cornerRadius = strenghView.bounds.height/2 + } + } + + // MARK: - Private + + private func updateStrengthColors() { + let strengthViewIndex: Int + let color: UIColor + + switch self.strength { + case .tooGuessable, .veryGuessable: + strengthViewIndex = 0 + color = StrengthColors.red + case .somewhatGuessable: + strengthViewIndex = 1 + color = StrengthColors.orange + case .safelyUnguessable: + strengthViewIndex = 2 + color = StrengthColors.yellow + case .veryUnguessable: + strengthViewIndex = 3 + color = StrengthColors.green + } + + self.color(until: strengthViewIndex, with: color) + } + + private func color(until strengthViewIndex: Int, with color: UIColor) { + var index: Int = 0 + + for strenghView in self.strengthViews { + + let strenghViewBackgroundColor: UIColor + + if index <= strengthViewIndex { + strenghViewBackgroundColor = color + } else { + strenghViewBackgroundColor = StrengthColors.gray + } + + strenghView.backgroundColor = strenghViewBackgroundColor + + index+=1 + } + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.xib b/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.xib new file mode 100644 index 000000000..2ab94ba6c --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/PasswordStrengthView.xib @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyViewController.swift b/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyViewController.swift new file mode 100644 index 000000000..9f7ba38be --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/RecoveryKey/KeyBackupSetupRecoveryKeyViewController.swift @@ -0,0 +1,234 @@ +/* + 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 KeyBackupSetupRecoveryKeyViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let animationDuration: TimeInterval = 0.3 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var recoveryKeyBackgroundView: UIView! + + @IBOutlet private weak var recoveryKeyTitleLabel: UILabel! + @IBOutlet private weak var recoveryKeyLabel: UILabel! + + @IBOutlet private weak var separatorView: UIView! + + @IBOutlet private weak var makeCopyButton: UIButton! + + @IBOutlet private weak var madeCopyButtonBackgroundView: UIView! + @IBOutlet private weak var madeCopyButton: UIButton! + + // MARK: Private + + private var theme: Theme! + private var hasMadeACopy: Bool = false + private var viewModel: KeyBackupSetupRecoveryKeyViewModelType! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + private weak var skipAlertController: UIAlertController? + + // MARK: - Setup + + class func instantiate(with viewModel: KeyBackupSetupRecoveryKeyViewModelType) -> KeyBackupSetupRecoveryKeyViewController { + let viewController = StoryboardScene.KeyBackupSetupRecoveryKeyViewController.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.keyBackupSetupTitle + + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + 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) + } + + self.informationLabel.textColor = theme.textPrimaryColor + + self.recoveryKeyBackgroundView.backgroundColor = theme.backgroundColor + + self.recoveryKeyTitleLabel.textColor = theme.textPrimaryColor + self.recoveryKeyLabel.textColor = theme.textPrimaryColor + + self.separatorView.backgroundColor = theme.lineBreakColor + + theme.applyStyle(onButton: self.makeCopyButton) + + self.madeCopyButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.madeCopyButton) + } + + 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 skipBarButtonItem = MXKBarButtonItem(title: VectorL10n.keyBackupSetupSkipAction, style: .plain) { [weak self] in + self?.skipButtonAction() + } + self.navigationItem.rightBarButtonItem = skipBarButtonItem + + self.informationLabel.text = VectorL10n.keyBackupSetupRecoveryKeyInfo + self.recoveryKeyTitleLabel.text = VectorL10n.keyBackupSetupRecoveryKeyRecoveryKeyTitle + self.recoveryKeyLabel.text = self.viewModel.recoveryKey + + self.makeCopyButton.setTitle(VectorL10n.keyBackupSetupRecoveryKeyMakeCopyAction, for: .normal) + self.madeCopyButton.setTitle(VectorL10n.keyBackupSetupRecoveryKeyMadeCopyAction, for: .normal) + + self.updateMadeCopyButton() + } + + private func shareRecoveryKey() { + + // Set up activity view controller + let activityItems: [Any] = [ self.viewModel.recoveryKey ] + let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) + + activityViewController.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in + + // Enable made copy button only if user has selected an activity item + if completed { + self.hasMadeACopy = true + self.updateMadeCopyButton() + } + } + + // Configure source view when activity view controller is presented with a popover + if let popoverPresentationController = activityViewController.popoverPresentationController { + popoverPresentationController.sourceView = self.makeCopyButton + popoverPresentationController.sourceRect = self.makeCopyButton.bounds + popoverPresentationController.permittedArrowDirections = [.down, .up] + } + + self.present(activityViewController, animated: true) + } + + private func updateMadeCopyButton() { + self.madeCopyButton.isEnabled = self.hasMadeACopy + } + + private func render(viewState: KeyBackupSetupRecoveryKeyViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.view.endEditing(true) + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + private func showSkipAlert() { + guard self.skipAlertController == nil else { + return + } + + let alertController = UIAlertController(title: VectorL10n.keyBackupSetupSkipAlertTitle, + message: VectorL10n.keyBackupSetupSkipAlertMessage, + preferredStyle:.alert) + + alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in + self.viewModel.process(viewAction: .skipAlertContinue) + })) + + alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction, style: .default, handler: { action in + self.viewModel.process(viewAction: .skipAlertSkip) + })) + + self.present(alertController, animated: true, completion: nil) + self.skipAlertController = alertController + } + + // MARK: - Actions + + @IBAction private func makeCopyButtonAction(_ sender: Any) { + self.shareRecoveryKey() + } + + @IBAction private func madeCopyButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .madeCopy) + } + + private func skipButtonAction() { + self.viewModel.process(viewAction: .skip) + } +} + +// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate +extension KeyBackupSetupRecoveryKeyViewController: KeyBackupSetupRecoveryKeyViewModelViewDelegate { + func keyBackupSetupRecoveryKeyViewModel(_ viewModel: KeyBackupSetupRecoveryKeyViewModelType, didUpdateViewState viewSate: KeyBackupSetupRecoveryKeyViewState) { + self.render(viewState: viewSate) + } + + func keyBackupSetupPassphraseViewModelShowSkipAlert(_ viewModel: KeyBackupSetupRecoveryKeyViewModelType) { + self.showSkipAlert() + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.storyboard b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.storyboard new file mode 100644 index 000000000..23350ef8a --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.storyboard @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift new file mode 100644 index 000000000..c7e15b64d --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift @@ -0,0 +1,149 @@ +/* + 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 + +protocol KeyBackupSetupSuccessFromPassphraseViewControllerDelegate: class { + func keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromPassphraseViewController) +} + +final class KeyBackupSetupSuccessFromPassphraseViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var keyBackupLogoImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var saveRecoveryKeyButtonBackgroundView: UIView! + @IBOutlet private weak var saveRecoveryKeyButton: UIButton! + + @IBOutlet private weak var doneButtonBackgroundView: UIView! + @IBOutlet private weak var doneButton: UIButton! + + // MARK: Private + + private var theme: Theme! + private var recoveryKey: String! + + // MARK: Public + + weak var delegate: KeyBackupSetupSuccessFromPassphraseViewControllerDelegate? + + // MARK: - Setup + + class func instantiate(with recoveryKey: String) -> KeyBackupSetupSuccessFromPassphraseViewController { + let viewController = StoryboardScene.KeyBackupSetupSuccessFromPassphraseViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + viewController.recoveryKey = recoveryKey + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = VectorL10n.keyBackupSetupTitle + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + + let keybackupLogoImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.keyBackupLogoImageView.image = keybackupLogoImage + + self.titleLabel.text = VectorL10n.keyBackupSetupSuccessTitle + self.informationLabel.text = VectorL10n.keyBackupSetupSuccessFromPassphraseInfo + + self.saveRecoveryKeyButton.setTitle(VectorL10n.keyBackupSetupSuccessFromPassphraseSaveRecoveryKeyAction, for: .normal) + self.doneButton.setTitle(VectorL10n.keyBackupSetupSuccessFromPassphraseDoneAction, for: .normal) + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.keyBackupLogoImageView.tintColor = theme.textPrimaryColor + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + + self.saveRecoveryKeyButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.saveRecoveryKeyButton) + + self.doneButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.doneButton) + } + + 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 shareRecoveryKey() { + + // Set up activity view controller + let activityItems: [Any] = [ self.recoveryKey ] + let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) + + // Configure source view when activity view controller is presented with a popover + if let popoverPresentationController = activityViewController.popoverPresentationController { + popoverPresentationController.sourceView = self.saveRecoveryKeyButton + popoverPresentationController.sourceRect = self.saveRecoveryKeyButton.bounds + popoverPresentationController.permittedArrowDirections = [.down, .up] + } + + self.present(activityViewController, animated: true) + } + + // MARK: - Actions + + @IBAction private func saveRecoveryKeyButtonAction(_ sender: Any) { + self.shareRecoveryKey() + } + + @IBAction private func doneButtonAction(_ sender: Any) { + self.delegate?.keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard new file mode 100644 index 000000000..7ba44ec68 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.storyboard @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift new file mode 100644 index 000000000..6f474f753 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift @@ -0,0 +1,181 @@ +/* + 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 + +protocol KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate: class { + func keyBackupSetupSuccessFromRecoveryKeyViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromRecoveryKeyViewController) +} + +final class KeyBackupSetupSuccessFromRecoveryKeyViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var keyBackupLogoImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var recoveryKeySectionBackgroundView: UIView! + + @IBOutlet private weak var recoveryKeyTitleLabel: UILabel! + @IBOutlet private weak var recoveryKeyLabel: UILabel! + + @IBOutlet private weak var separatorView: UIView! + + @IBOutlet private weak var makeACopyButton: UIButton! + + @IBOutlet private weak var madeACopyButtonBackgroundView: UIView! + @IBOutlet private weak var madeACopyButton: UIButton! + + // MARK: Private + + private var theme: Theme! + private var recoveryKey: String! + private var hasMadeARecoveryKeyCopy: Bool = false + + // MARK: Public + + weak var delegate: KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate? + + // MARK: - Setup + + class func instantiate(with recoveryKey: String) -> KeyBackupSetupSuccessFromRecoveryKeyViewController { + let viewController = StoryboardScene.KeyBackupSetupSuccessFromRecoveryKeyViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + viewController.recoveryKey = recoveryKey + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = VectorL10n.keyBackupSetupTitle + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + + let keybackupLogoImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.keyBackupLogoImageView.image = keybackupLogoImage + + self.titleLabel.text = VectorL10n.keyBackupSetupSuccessTitle + self.informationLabel.text = VectorL10n.keyBackupSetupSuccessFromRecoveryKeyInfo + + self.recoveryKeyTitleLabel.text = VectorL10n.keyBackupSetupSuccessFromRecoveryKeyRecoveryKeyTitle + self.recoveryKeyLabel.text = self.recoveryKey + + self.makeACopyButton.setTitle(VectorL10n.keyBackupSetupSuccessFromRecoveryKeyMakeCopyAction, for: .normal) + self.madeACopyButton.setTitle(VectorL10n.keyBackupSetupSuccessFromRecoveryKeyMadeCopyAction, for: .normal) + + self.updateDoneButton() + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.keyBackupLogoImageView.tintColor = theme.textPrimaryColor + + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + + self.recoveryKeySectionBackgroundView.backgroundColor = theme.backgroundColor + + self.recoveryKeyTitleLabel.textColor = theme.textPrimaryColor + self.recoveryKeyLabel.textColor = theme.textPrimaryColor + + self.separatorView.backgroundColor = theme.lineBreakColor + + theme.applyStyle(onButton: self.makeACopyButton) + + self.madeACopyButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.madeACopyButton) + } + + 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 shareRecoveryKey() { + + // Set up activity view controller + let activityItems: [Any] = [ self.recoveryKey ] + let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) + + activityViewController.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in + + // Enable made copy button only if user has selected an activity item and has setup recovery key without passphrase + if completed { + self.hasMadeARecoveryKeyCopy = true + self.updateDoneButton() + } + } + + // Configure source view when activity view controller is presented with a popover + if let popoverPresentationController = activityViewController.popoverPresentationController { + popoverPresentationController.sourceView = self.makeACopyButton + popoverPresentationController.sourceRect = self.makeACopyButton.bounds + popoverPresentationController.permittedArrowDirections = [.down, .up] + } + + self.present(activityViewController, animated: true) + } + + private func updateDoneButton() { + self.madeACopyButton.isEnabled = self.hasMadeARecoveryKeyCopy + } + + // MARK: - Actions + + @IBAction private func saveRecoveryKeyButtonAction(_ sender: Any) { + self.shareRecoveryKey() + } + + @IBAction private func doneButtonAction(_ sender: Any) { + self.delegate?.keyBackupSetupSuccessFromRecoveryKeyViewControllerDidTapDoneAction(self) + } +} diff --git a/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m b/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m index 4ed3f011f..f74637cde 100644 --- a/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m +++ b/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m @@ -21,6 +21,8 @@ #import +#import "Riot-Swift.h" + @interface MediaAlbumContentViewController () { /** @@ -39,9 +41,9 @@ NSMutableArray *selectedAssets; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -101,7 +103,7 @@ } // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -111,15 +113,15 @@ - (void)userInterfaceThemeDidChange { - self.assetsCollectionView.backgroundColor = kRiotPrimaryBgColor; - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.assetsCollectionView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -139,10 +141,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } diff --git a/Riot/Modules/MediaPicker/MediaPickerViewController.m b/Riot/Modules/MediaPicker/MediaPickerViewController.m index f0f30b847..ff48c46b0 100644 --- a/Riot/Modules/MediaPicker/MediaPickerViewController.m +++ b/Riot/Modules/MediaPicker/MediaPickerViewController.m @@ -18,6 +18,7 @@ #import "MediaPickerViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import @@ -83,9 +84,9 @@ static void *RecordingContext = &RecordingContext; NSDate *videoRecordStartDate; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; /** The current visibility of the status bar in this view controller. @@ -171,7 +172,7 @@ static void *RecordingContext = &RecordingContext; }]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -181,20 +182,20 @@ static void *RecordingContext = &RecordingContext; - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.cameraVideoCaptureProgressView.progressColor = kRiotPrimaryBgColor; + self.cameraVideoCaptureProgressView.progressColor = ThemeService.shared.theme.backgroundColor; self.cameraVideoCaptureProgressView.unprogressColor = [UIColor clearColor]; - self.userAlbumsTableView.backgroundColor = kRiotPrimaryBgColor; - self.view.backgroundColor = kRiotPrimaryBgColor; + self.userAlbumsTableView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -306,7 +307,7 @@ static void *RecordingContext = &RecordingContext; - (void)checkDeviceAuthorizationStatus { - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; [MXKTools checkAccessForMediaType:AVMediaTypeVideo manualChangeMessage:[NSString stringWithFormat:NSLocalizedStringFromTable(@"camera_access_not_granted", @"Vector", nil), appDisplayName] @@ -342,12 +343,12 @@ static void *RecordingContext = &RecordingContext; if (self.cameraVideoCaptureProgressView.progressColor != [UIColor lightGrayColor]) { self.cameraVideoCaptureProgressView.progressColor = [UIColor lightGrayColor]; - self.cameraVideoCaptureProgressView.unprogressColor = kRiotPrimaryBgColor; + self.cameraVideoCaptureProgressView.unprogressColor = ThemeService.shared.theme.backgroundColor; } } - else if (self.cameraVideoCaptureProgressView.progressColor != kRiotPrimaryBgColor) + else if (self.cameraVideoCaptureProgressView.progressColor != ThemeService.shared.theme.backgroundColor) { - self.cameraVideoCaptureProgressView.progressColor = kRiotPrimaryBgColor; + self.cameraVideoCaptureProgressView.progressColor = ThemeService.shared.theme.backgroundColor; self.cameraVideoCaptureProgressView.unprogressColor = [UIColor lightGrayColor]; } @@ -1004,10 +1005,10 @@ static void *RecordingContext = &RecordingContext; { [self stopAVCapture]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (UIApplicationWillEnterForegroundNotificationObserver) @@ -1324,7 +1325,7 @@ static void *RecordingContext = &RecordingContext; - (void)caughtAVRuntimeError:(NSNotification*)note { - NSError *error = [[note userInfo] objectForKey:AVCaptureSessionErrorKey]; + NSError *error = [note userInfo][AVCaptureSessionErrorKey]; NSLog(@"[MediaPickerVC] AV Session Error: %@", error); dispatch_async(dispatch_get_main_queue(), ^{ @@ -1764,13 +1765,13 @@ static void *RecordingContext = &RecordingContext; - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -1838,7 +1839,7 @@ static void *RecordingContext = &RecordingContext; { if (validationView) { - validationView.image = [[notification userInfo] objectForKey:MPMoviePlayerThumbnailImageKey]; + validationView.image = [notification userInfo][MPMoviePlayerThumbnailImageKey]; [validationView bringSubviewToFront:videoPlayerControl]; // Now, there is a thumbnail, show the video control diff --git a/Riot/Modules/MediaPicker/Views/MediaAlbumTableCell.m b/Riot/Modules/MediaPicker/Views/MediaAlbumTableCell.m index a728a77b2..2f2f1e40a 100644 --- a/Riot/Modules/MediaPicker/Views/MediaAlbumTableCell.m +++ b/Riot/Modules/MediaPicker/Views/MediaAlbumTableCell.m @@ -17,7 +17,8 @@ #import "MediaAlbumTableCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation MediaAlbumTableCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.albumDisplayNameLabel.textColor = kRiotPrimaryTextColor; - self.albumCountLabel.textColor = kRiotSecondaryTextColor; + self.albumDisplayNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.albumCountLabel.textColor = ThemeService.shared.theme.textSecondaryColor; } @end diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index f2fb57b10..84ec480a7 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -108,8 +108,7 @@ } [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_people", @"Vector", nil); - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorOrange; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorOrange; + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorOrange; if (recentsDataSource) { @@ -119,18 +118,6 @@ } } -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - - if ([AppDelegate theDelegate].masterTabBarController.tabBar.tintColor == kRiotColorOrange) - { - // Restore default tintColor - [AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar.tintColor = kRiotColorGreen; - [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = kRiotColorGreen; - } -} - #pragma mark - - (void)displayList:(MXKRecentsDataSource *)listDataSource diff --git a/Riot/Modules/People/Views/InviteRecentTableViewCell.m b/Riot/Modules/People/Views/InviteRecentTableViewCell.m index a942bf7f5..b9a7de18b 100644 --- a/Riot/Modules/People/Views/InviteRecentTableViewCell.m +++ b/Riot/Modules/People/Views/InviteRecentTableViewCell.m @@ -21,7 +21,8 @@ #import "MXEvent.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #pragma mark - Constant definitions @@ -59,10 +60,10 @@ NSString *const kInviteRecentTableViewCellRoomKey = @"kInviteRecentTableViewCell { [super customizeTableViewCellRendering]; - self.leftButton.backgroundColor = kRiotColorGreen; - self.rightButton.backgroundColor = kRiotColorGreen; + self.leftButton.backgroundColor = ThemeService.shared.theme.tintColor; + self.rightButton.backgroundColor = ThemeService.shared.theme.tintColor; - self.noticeBadgeView.backgroundColor = kRiotColorPinkRed; + self.noticeBadgeView.backgroundColor = ThemeService.shared.theme.noticeColor; } - (void)onDeclinePressed:(id)sender diff --git a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m index 82fb580a9..7f490e176 100644 --- a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m +++ b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m @@ -20,6 +20,7 @@ #import "PublicRoomTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #pragma mark - Constants definitions @@ -338,7 +339,7 @@ double const kPublicRoomsDirectoryDataExpiration = 10; if (!tableViewCell) { tableViewCell = [[MXKTableViewCell alloc] init]; - tableViewCell.textLabel.textColor = kRiotSecondaryTextColor; + tableViewCell.textLabel.textColor = ThemeService.shared.theme.textSecondaryColor; tableViewCell.textLabel.font = [UIFont systemFontOfSize:15.0]; tableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; } diff --git a/Riot/Modules/PublicRoomList/Views/PublicRoomTableViewCell.m b/Riot/Modules/PublicRoomList/Views/PublicRoomTableViewCell.m index a8fbca4cc..4195b241b 100644 --- a/Riot/Modules/PublicRoomList/Views/PublicRoomTableViewCell.m +++ b/Riot/Modules/PublicRoomList/Views/PublicRoomTableViewCell.m @@ -20,7 +20,8 @@ #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation PublicRoomTableViewCell @@ -37,9 +38,9 @@ { [super customizeTableViewCellRendering]; - self.roomDisplayName.textColor = kRiotPrimaryTextColor; - self.roomTopic.textColor = kRiotSecondaryTextColor; - self.memberCount.textColor = kRiotSecondaryTextColor; + self.roomDisplayName.textColor = ThemeService.shared.theme.textPrimaryColor; + self.roomTopic.textColor = ThemeService.shared.theme.textSecondaryColor; + self.memberCount.textColor = ThemeService.shared.theme.textSecondaryColor; _roomAvatar.defaultBackgroundColor = [UIColor clearColor]; } diff --git a/Riot/Modules/Room/Attachements/AttachmentsViewController.m b/Riot/Modules/Room/Attachements/AttachmentsViewController.m index 72f2dd5b0..33637679b 100644 --- a/Riot/Modules/Room/Attachements/AttachmentsViewController.m +++ b/Riot/Modules/Room/Attachements/AttachmentsViewController.m @@ -18,11 +18,12 @@ #import "AttachmentsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface AttachmentsViewController () { - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -48,7 +49,7 @@ self.attachmentsCollection.accessibilityIdentifier =@"AttachmentsVC"; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -58,14 +59,12 @@ - (void)userInterfaceThemeDidChange { - self.view.backgroundColor = kRiotPrimaryBgColor; - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.navigationBar.tintColor = kRiotSecondaryBgColor; - self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor}; - self.backButton.tintColor = kRiotColorGreen; + self.backButton.tintColor = ThemeService.shared.theme.tintColor; } - (void)viewWillAppear:(BOOL)animated @@ -80,10 +79,10 @@ { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 55aa3051e..81cbef55f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -275,7 +275,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; for (; index < bubbleComponents.count; index++) { // Compute the vertical position for next component - component = [bubbleComponents objectAtIndex:index]; + component = bubbleComponents[index]; component.position = CGPointMake(0, positionY); @@ -316,7 +316,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; for (index++; index < bubbleComponents.count; index++) { // Compute the vertical position for next component - component = [bubbleComponents objectAtIndex:index]; + component = bubbleComponents[index]; if (component.attributedTextMessage) { diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 24a60e6fd..03457f1dd 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -22,14 +22,15 @@ #import "MXKRoomBubbleTableViewCell+Riot.h" #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXRoom+Riot.h" @interface RoomDataSource() { - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -58,7 +59,7 @@ self.markTimelineInitialEvent = NO; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { // Force room data reload. [self updateEventFormatter]; @@ -105,10 +106,10 @@ - (void)destroy { - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [super destroy]; @@ -385,7 +386,7 @@ if ([component.event.eventId isEqualToString:self.room.accountData.readMarkerEventId]) { bubbleCell.readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - 2, bubbleCell.bubbleOverlayContainer.frame.size.width, 2)]; - bubbleCell.readMarkerView.backgroundColor = kRiotColorGreen; + bubbleCell.readMarkerView.backgroundColor = ThemeService.shared.theme.tintColor; // Hide by default the marker, it will be shown and animated when the cell will be rendered. bubbleCell.readMarkerView.hidden = YES; bubbleCell.readMarkerView.tag = index; diff --git a/Riot/Modules/Room/Files/RoomFilesViewController.m b/Riot/Modules/Room/Files/RoomFilesViewController.m index 53319ec57..4fb62f934 100644 --- a/Riot/Modules/Room/Files/RoomFilesViewController.m +++ b/Riot/Modules/Room/Files/RoomFilesViewController.m @@ -20,15 +20,16 @@ #import "FilesSearchTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "AttachmentsViewController.h" @interface RoomFilesViewController () { /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -99,7 +100,7 @@ [UIView setAnimationsEnabled:YES]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -109,12 +110,12 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.bubblesTableView.backgroundColor; if (self.bubblesTableView.dataSource) @@ -125,17 +126,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -192,13 +193,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m index 5b88317b3..7837018ee 100644 --- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m +++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m @@ -19,6 +19,7 @@ #import "RoomMemberDetailsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "RoomMemberTitleView.h" @@ -69,9 +70,9 @@ id UIApplicationWillChangeStatusBarOrientationNotificationObserver; /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; /** The current visibility of the status bar in this view controller. @@ -207,7 +208,7 @@ }]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -217,16 +218,18 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + self.navigationController.navigationBar.translucent = YES; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.memberHeaderView.backgroundColor = kRiotSecondaryBgColor; - self.roomMemberNameLabel.textColor = kRiotPrimaryTextColor; - self.roomMemberStatusLabel.textColor = kRiotColorGreen; + self.memberHeaderView.backgroundColor = ThemeService.shared.theme.baseColor; + self.roomMemberNameLabel.textColor = ThemeService.shared.theme.baseTextPrimaryColor; + + self.roomMemberStatusLabel.textColor = ThemeService.shared.theme.tintColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -237,7 +240,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (BOOL)prefersStatusBarHidden @@ -301,10 +304,10 @@ UIApplicationWillChangeStatusBarOrientationNotificationObserver = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [memberTitleView removeFromSuperview]; @@ -357,7 +360,8 @@ return [AvatarGenerator generateAvatarForMatrixItem:self.mxRoomMember.userId withDisplayName:self.mxRoomMember.displayname]; } - return [UIImage imageNamed:@"placeholder"]; + return [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor]; } - (void)updateMemberInfo @@ -373,12 +377,12 @@ MXRoomPowerLevels *powerLevels = [roomState powerLevels]; NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId]; - if (powerLevel >= kRiotRoomAdminLevel) + if (powerLevel >= RoomPowerLevelAdmin) { self->memberTitleView.memberBadge.image = [UIImage imageNamed:@"admin_icon"]; self->memberTitleView.memberBadge.hidden = NO; } - else if (powerLevel >= kRiotRoomModeratorLevel) + else if (powerLevel >= RoomPowerLevelModerator) { self->memberTitleView.memberBadge.image = [UIImage imageNamed:@"mod_icon"]; self->memberTitleView.memberBadge.hidden = NO; @@ -471,6 +475,9 @@ [mainNavigationController.navigationBar setShadowImage:nil]; [mainNavigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; } + + // Main Navigation bar opacity must follow + mainNavigationController.navigationBar.translucent = isHidden; } #pragma mark - TableView data source @@ -499,13 +506,13 @@ if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels]) { // Check whether the user is admin (in this case he may reduce his power level to become moderator or less, EXCEPT if he is the only admin). - if (oneSelfPowerLevel >= kRiotRoomAdminLevel) + if (oneSelfPowerLevel >= RoomPowerLevelAdmin) { NSArray *levelValues = powerLevels.users.allValues; NSUInteger adminCount = 0; for (NSNumber *valueNumber in levelValues) { - if ([valueNumber unsignedIntegerValue] >= kRiotRoomAdminLevel) + if ([valueNumber unsignedIntegerValue] >= RoomPowerLevelAdmin) { adminCount ++; } @@ -518,7 +525,7 @@ } } // Check whether the user is moderator (in this case he may reduce his power level to become normal user). - else if (oneSelfPowerLevel >= kRiotRoomModeratorLevel) + else if (oneSelfPowerLevel >= RoomPowerLevelModerator) { [adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetDefaultPowerLevel)]; } @@ -536,18 +543,18 @@ if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels] && oneSelfPowerLevel > memberPowerLevel) { // Check whether user is admin - if (oneSelfPowerLevel >= kRiotRoomAdminLevel) + if (oneSelfPowerLevel >= RoomPowerLevelAdmin) { [adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetAdmin)]; } // Check whether the member may become moderator - if (oneSelfPowerLevel >= kRiotRoomModeratorLevel && memberPowerLevel < kRiotRoomModeratorLevel) + if (oneSelfPowerLevel >= RoomPowerLevelModerator && memberPowerLevel < RoomPowerLevelModerator) { [adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetModerator)]; } - if (memberPowerLevel >= kRiotRoomModeratorLevel) + if (memberPowerLevel >= RoomPowerLevelModerator) { [adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetDefaultPowerLevel)]; } @@ -754,11 +761,11 @@ NSNumber *actionNumber; if (indexPath.section == adminToolsIndex && indexPath.row < adminActionsArray.count) { - actionNumber = [adminActionsArray objectAtIndex:indexPath.row]; + actionNumber = adminActionsArray[indexPath.row]; } else if (indexPath.section == otherActionsIndex && indexPath.row < otherActionsArray.count) { - actionNumber = [otherActionsArray objectAtIndex:indexPath.row]; + actionNumber = otherActionsArray[indexPath.row]; } if (actionNumber) @@ -770,13 +777,13 @@ if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionKick) { - [cellWithButton.mxkButton setTitleColor:kRiotColorPinkRed forState:UIControlStateNormal]; - [cellWithButton.mxkButton setTitleColor:kRiotColorPinkRed forState:UIControlStateHighlighted]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.warningColor forState:UIControlStateNormal]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.warningColor forState:UIControlStateHighlighted]; } else { - [cellWithButton.mxkButton setTitleColor:kRiotPrimaryTextColor forState:UIControlStateNormal]; - [cellWithButton.mxkButton setTitleColor:kRiotPrimaryTextColor forState:UIControlStateHighlighted]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateNormal]; + [cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateHighlighted]; } [cellWithButton.mxkButton addTarget:self action:@selector(onActionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; @@ -837,13 +844,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -942,12 +949,12 @@ } case MXKRoomMemberDetailsActionSetModerator: { - [self setPowerLevel:kRiotRoomModeratorLevel promptUser:YES]; + [self setPowerLevel:RoomPowerLevelModerator promptUser:YES]; break; } case MXKRoomMemberDetailsActionSetAdmin: { - [self setPowerLevel:kRiotRoomAdminLevel promptUser:YES]; + [self setPowerLevel:RoomPowerLevelAdmin promptUser:YES]; break; } case MXKRoomMemberDetailsActionBan: @@ -983,13 +990,15 @@ if (weakSelf) { typeof(self) self = weakSelf; + + NSString *text = [self->currentAlert textFields].firstObject.text; + self->currentAlert = nil; [self startActivityIndicator]; // kick user - UITextField *textField = [self->currentAlert textFields].firstObject; - [self.mxRoom banUser:self.mxRoomMember.userId reason:textField.text success:^{ + [self.mxRoom banUser:self.mxRoomMember.userId reason:text success:^{ __strong __typeof(weakSelf)self = weakSelf; [self stopActivityIndicator]; @@ -1045,13 +1054,15 @@ if (weakSelf) { typeof(self) self = weakSelf; + + NSString *text = [self->currentAlert textFields].firstObject.text; + self->currentAlert = nil; [self startActivityIndicator]; // kick user - UITextField *textField = [self->currentAlert textFields].firstObject; - [self.mxRoom kickUser:self.mxRoomMember.userId reason:textField.text success:^{ + [self.mxRoom kickUser:self.mxRoomMember.userId reason:text success:^{ __strong __typeof(weakSelf)self = weakSelf; [self stopActivityIndicator]; diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.m b/Riot/Modules/Room/Members/RoomParticipantsViewController.m index 3a3fefa6a..14b54210f 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.m +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.m @@ -20,6 +20,7 @@ #import "RoomMemberDetailsViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "Contact.h" @@ -60,8 +61,8 @@ UIAlertController *currentAlert; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -146,7 +147,7 @@ [self addAddParticipantButton]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -156,24 +157,24 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; + _searchBarHeaderBorder.backgroundColor = ThemeService.shared.theme.headerBorderColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; // Update the gradient view above the screen CGFloat white = 1.0; - [kRiotPrimaryBgColor getWhite:&white alpha:nil]; + [ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil]; CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor; CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor; - tableViewMaskLayer.colors = [NSArray arrayWithObjects:(__bridge id)transparentWhiteColor, (__bridge id)transparentWhiteColor, (__bridge id)opaqueWhiteColor, nil]; + tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor]; if (self.tableView.dataSource) { @@ -183,7 +184,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } // This method is called when the viewcontroller is added or removed from a container view controller. @@ -196,10 +197,10 @@ - (void)destroy { - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (leaveRoomNotificationObserver) @@ -574,20 +575,19 @@ // Add blur mask programmatically tableViewMaskLayer = [CAGradientLayer layer]; - // Consider the grayscale components of the kRiotPrimaryBgColor. + // Consider the grayscale components of the ThemeService.shared.theme.backgroundColor. CGFloat white = 1.0; - [kRiotPrimaryBgColor getWhite:&white alpha:nil]; + [ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil]; CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor; CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor; - tableViewMaskLayer.colors = [NSArray arrayWithObjects:(__bridge id)transparentWhiteColor, (__bridge id)transparentWhiteColor, (__bridge id)opaqueWhiteColor, nil]; + tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor]; // display a gradient to the rencents bottom (20% of the bottom of the screen) - tableViewMaskLayer.locations = [NSArray arrayWithObjects: - [NSNumber numberWithFloat:0], - [NSNumber numberWithFloat:0.85], - [NSNumber numberWithFloat:1.0], nil]; + tableViewMaskLayer.locations = @[@0.0F, + @0.85F, + @1.0F]; tableViewMaskLayer.bounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); tableViewMaskLayer.anchorPoint = CGPointZero; @@ -671,18 +671,18 @@ // List all the participants matrix user id to ignore them during the contacts search. for (Contact *contact in actualParticipants) { - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxMember.userId]; + contactsDataSource.ignoredContactsByMatrixId[contact.mxMember.userId] = contact; } for (Contact *contact in invitedParticipants) { if (contact.mxMember) { - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxMember.userId]; + contactsDataSource.ignoredContactsByMatrixId[contact.mxMember.userId] = contact; } } if (userParticipant) { - [contactsDataSource.ignoredContactsByMatrixId setObject:userParticipant forKey:userParticipant.mxMember.userId]; + contactsDataSource.ignoredContactsByMatrixId[userParticipant.mxMember.userId] = userParticipant; } [contactsPickerViewController showSearch:YES]; @@ -1127,12 +1127,12 @@ // Update member badge MXRoomPowerLevels *powerLevels = [roomState powerLevels]; NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:contact.mxMember.userId]; - if (powerLevel >= kRiotRoomAdminLevel) + if (powerLevel >= RoomPowerLevelAdmin) { participantCell.thumbnailBadgeView.image = [UIImage imageNamed:@"admin_icon"]; participantCell.thumbnailBadgeView.hidden = NO; } - else if (powerLevel >= kRiotRoomModeratorLevel) + else if (powerLevel >= RoomPowerLevelModerator) { participantCell.thumbnailBadgeView.image = [UIImage imageNamed:@"mod_icon"]; participantCell.thumbnailBadgeView.hidden = NO; @@ -1175,13 +1175,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -1215,7 +1215,7 @@ if (section == invitedSection) { sectionHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 30)]; - sectionHeader.backgroundColor = kRiotSecondaryBgColor; + sectionHeader.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; CGRect frame = sectionHeader.frame; frame.origin.x = 20; @@ -1223,7 +1223,7 @@ frame.size.width = sectionHeader.frame.size.width - 10; frame.size.height -= 10; UILabel *headerLabel = [[UILabel alloc] initWithFrame:frame]; - headerLabel.textColor = kRiotPrimaryTextColor; + headerLabel.textColor = ThemeService.shared.theme.textPrimaryColor; headerLabel.font = [UIFont boldSystemFontOfSize:15.0]; headerLabel.backgroundColor = [UIColor clearColor]; @@ -1327,7 +1327,7 @@ }]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:leaveAction atIndex:0]; } @@ -1684,19 +1684,19 @@ - (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar { // bar tint color - searchBar.barTintColor = searchBar.tintColor = kRiotColorGreen; - searchBar.tintColor = kRiotColorGreen; + searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.theme.tintColor; + searchBar.tintColor = ThemeService.shared.theme.tintColor; // FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals. // text color UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"]; - searchBarTextField.textColor = kRiotSecondaryTextColor; + searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor; // Magnifying glass icon. UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView; leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - leftImageView.tintColor = kRiotColorGreen; + leftImageView.tintColor = ThemeService.shared.theme.tintColor; // remove the gray background color UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"]; @@ -1707,8 +1707,8 @@ // place holder searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), - NSUnderlineColorAttributeName: kRiotColorGreen, - NSForegroundColorAttributeName: kRiotColorGreen}]; + NSUnderlineColorAttributeName: ThemeService.shared.theme.tintColor, + NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText diff --git a/Riot/Modules/Room/ReadReceiptsDetail/ReadReceiptsViewController.m b/Riot/Modules/Room/ReadReceiptsDetail/ReadReceiptsViewController.m index 09ffafd0c..432d5f6c9 100644 --- a/Riot/Modules/Room/ReadReceiptsDetail/ReadReceiptsViewController.m +++ b/Riot/Modules/Room/ReadReceiptsDetail/ReadReceiptsViewController.m @@ -19,12 +19,13 @@ #import #import "RageShakeManager.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @interface ReadReceiptsViewController () { - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @property (nonatomic) MXSession *session; @@ -77,7 +78,7 @@ [self addOverlayViewGesture]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -93,20 +94,20 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.overlayView.backgroundColor = kRiotOverlayColor; + self.overlayView.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; self.overlayView.alpha = 1.0; - self.titleLabel.textColor = kRiotPrimaryTextColor; - self.containerView.backgroundColor = kRiotPrimaryBgColor; + self.titleLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.containerView.backgroundColor = ThemeService.shared.theme.backgroundColor; // Check the table view style to select its bg color. - self.receiptsTableView.backgroundColor = ((self.receiptsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.receiptsTableView.backgroundColor = ((self.receiptsTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); - self.closeButton.tintColor = kRiotColorGreen; + self.closeButton.tintColor = ThemeService.shared.theme.tintColor; if (self.receiptsTableView.dataSource) { @@ -117,15 +118,15 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } [super destroy]; @@ -195,8 +196,8 @@ { MXKReadReceiptTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MXKReadReceiptTableViewCell defaultReuseIdentifier] forIndexPath:indexPath]; - cell.displayNameLabel.textColor = kRiotPrimaryTextColor; - cell.receiptDescriptionLabel.textColor = kRiotSecondaryTextColor; + cell.displayNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + cell.receiptDescriptionLabel.textColor = ThemeService.shared.theme.textSecondaryColor; if (indexPath.row < self.roomMembers.count) { @@ -223,9 +224,9 @@ NSString *receiptReadText = NSLocalizedStringFromTable(@"receipt_status_read", @"Vector", nil); NSString *receiptTimeText = [(MXKEventFormatter*)self.session.roomSummaryUpdateDelegate dateStringFromTimestamp:self.receipts[indexPath.row].ts withTime:YES]; - NSMutableAttributedString *receiptDescription = [[NSMutableAttributedString alloc] initWithString:receiptReadText attributes:@{NSForegroundColorAttributeName : kRiotSecondaryTextColor, NSFontAttributeName : [UIFont boldSystemFontOfSize:15]}]; + NSMutableAttributedString *receiptDescription = [[NSMutableAttributedString alloc] initWithString:receiptReadText attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor, NSFontAttributeName : [UIFont boldSystemFontOfSize:15]}]; - [receiptDescription appendAttributedString:[[NSAttributedString alloc] initWithString:receiptTimeText attributes:@{NSForegroundColorAttributeName : kRiotSecondaryTextColor, NSFontAttributeName : [UIFont systemFontOfSize:15]}]]; + [receiptDescription appendAttributedString:[[NSAttributedString alloc] initWithString:receiptTimeText attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor, NSFontAttributeName : [UIFont systemFontOfSize:15]}]]; cell.receiptDescriptionLabel.attributedText = receiptDescription; } @@ -239,13 +240,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fea0b3ca0..3b7a6d701 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -203,8 +203,8 @@ // The right bar button items back up. NSArray *rightBarButtonItems; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; // Tell whether the input text field is in send reply mode. If true typed message will be sent to highlighted event. BOOL isInReplyMode; @@ -408,7 +408,7 @@ } // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -418,24 +418,42 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + // Consider the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = self.navigationController; + if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) + { + mainNavigationController = self.splitViewController.viewControllers.firstObject; + } + + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + if (mainNavigationController) + { + [ThemeService.shared.theme applyStyleOnNavigationBar:mainNavigationController.navigationBar]; + } + + // Keep navigation bar transparent in some cases + if (!self.expandedHeaderContainer.hidden || !self.previewHeaderContainer.hidden) + { + self.navigationController.navigationBar.translucent = YES; + mainNavigationController.navigationBar.translucent = YES; + } + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Prepare jump to last unread banner - self.jumpToLastUnreadBannerContainer.backgroundColor = kRiotPrimaryBgColor; - self.jumpToLastUnreadLabel.attributedText = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_jump_to_first_unread", @"Vector", nil) attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSUnderlineColorAttributeName: kRiotPrimaryTextColor, NSForegroundColorAttributeName: kRiotPrimaryTextColor}]; + self.jumpToLastUnreadBannerContainer.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.jumpToLastUnreadLabel.attributedText = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_jump_to_first_unread", @"Vector", nil) attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSUnderlineColorAttributeName: ThemeService.shared.theme.textPrimaryColor, NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor}]; - self.expandedHeaderContainer.backgroundColor = kRiotSecondaryBgColor; - self.previewHeaderContainer.backgroundColor = kRiotSecondaryBgColor; + self.expandedHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.previewHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - missedDiscussionsBadgeLabel.textColor = kRiotPrimaryBgColor; + missedDiscussionsBadgeLabel.textColor = ThemeService.shared.theme.backgroundColor; missedDiscussionsBadgeLabel.font = [UIFont boldSystemFontOfSize:14]; missedDiscussionsBadgeLabel.backgroundColor = [UIColor clearColor]; // Check the table view style to select its bg color. - self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.bubblesTableView.backgroundColor; if (self.bubblesTableView.dataSource) @@ -446,7 +464,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)didReceiveMemoryWarning @@ -566,35 +584,6 @@ } }]; [self refreshMissedDiscussionsCount:YES]; - - // Warn about the beta state of e2e encryption when entering the first time in an encrypted room - MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:self.roomDataSource.mxSession.myUser.userId]; - if (account && !account.isWarnedAboutEncryption && self.roomDataSource.room.summary.isEncrypted) - { - [currentAlert dismissViewControllerAnimated:NO completion:nil]; - - __weak __typeof(self) weakSelf = self; - currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"warning", @"Vector", nil) - message:NSLocalizedStringFromTable(@"room_warning_about_encryption", @"Vector", nil) - preferredStyle:UIAlertControllerStyleAlert]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - account.warnedAboutEncryption = YES; - } - - }]]; - - [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCEncryptionAlert"]; - [self presentViewController:currentAlert animated:YES completion:nil]; - } } - (void)viewDidDisappear:(BOOL)animated @@ -684,11 +673,13 @@ [expandedHeader layoutIfNeeded]; } } + + self.edgesForExtendedLayout = UIRectEdgeAll; // Adjust the top constraint of the bubbles table CGRect frame = expandedHeader.bottomBorderView.frame; self.expandedHeaderContainerHeightConstraint.constant = frame.origin.y + frame.size.height; - + self.bubblesTableViewTopConstraint.constant = self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.expandedHeaderContainerHeightConstraint.constant; } @@ -711,17 +702,23 @@ [previewHeader layoutIfNeeded]; } } - + + self.edgesForExtendedLayout = UIRectEdgeAll; + // Adjust the top constraint of the bubbles table CGRect frame = previewHeader.bottomBorderView.frame; self.previewHeaderContainerHeightConstraint.constant = frame.origin.y + frame.size.height; - + self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; } else { - self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; + // In non expanded header mode, the navigation bar is opaque + // The table view must not display behind it + self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight; + + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; // no expanded } [self refreshMissedDiscussionsCount:YES]; @@ -1141,10 +1138,10 @@ [self removeTypingNotificationsListener]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (kAppDelegateDidTapStatusBarNotificationObserver) { @@ -1330,7 +1327,7 @@ // Show it in red only for room widgets, not user's widgets // TODO: Design must be reviewed UIImage *icon = self.navigationItem.rightBarButtonItems[1].image; - icon = [MXKTools paintImage:icon withColor:kRiotColorPinkRed]; + icon = [MXKTools paintImage:icon withColor:ThemeService.shared.theme.warningColor]; icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; self.navigationItem.rightBarButtonItems[1].image = icon; @@ -1543,6 +1540,8 @@ // Report shadow image [mainNavigationController.navigationBar setShadowImage:shadowImage]; [mainNavigationController.navigationBar setBackgroundImage:shadowImage forBarMetrics:UIBarMetricsDefault]; + mainNavigationController.navigationBar.translucent = isVisible; + self.navigationController.navigationBar.translucent = isVisible; [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ @@ -1626,6 +1625,15 @@ } self.previewHeaderContainer.hidden = NO; + + // Consider the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = self.navigationController; + if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) + { + mainNavigationController = self.splitViewController.viewControllers.firstObject; + } + mainNavigationController.navigationBar.translucent = isVisible; + self.navigationController.navigationBar.translucent = isVisible; // Finalize preview header display according to the screen orientation [self refreshPreviewHeader:UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])]; @@ -1741,7 +1749,8 @@ } else { - previewHeader.roomAvatarPlaceholder = [UIImage imageNamed:@"placeholder"]; + previewHeader.roomAvatarPlaceholder = [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor]; } } @@ -1994,8 +2003,23 @@ } else if (tappedEvent) { - // Highlight this event in displayed message - [self selectEventWithId:tappedEvent.eventId]; + if (tappedEvent.eventType == MXEventTypeRoomCreate) + { + // Handle tap on RoomPredecessorBubbleCell + MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:tappedEvent.content]; + NSString *predecessorRoomId = createContent.roomPredecessorInfo.roomId; + + if (predecessorRoomId) + { + // Show predecessor room + [[AppDelegate theDelegate] showRoom:predecessorRoomId andEventId:nil withMatrixSession:self.mainSession]; + } + } + else + { + // Highlight this event in displayed message + [self selectEventWithId:tappedEvent.eventId]; + } } // Force table refresh @@ -2233,7 +2257,7 @@ [self cancelEventSelection]; - NSArray *activityItems = [NSArray arrayWithObjects:selectedComponent.textMessage, nil]; + NSArray *activityItems = @[selectedComponent.textMessage]; UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil]; @@ -2571,12 +2595,12 @@ if (weakSelf) { typeof(self) self = weakSelf; - UITextField *textField = [self->currentAlert textFields].firstObject; + NSString *text = [self->currentAlert textFields].firstObject.text; self->currentAlert = nil; [self startActivityIndicator]; - [self.roomDataSource.room reportEvent:selectedEvent.eventId score:-100 reason:textField.text success:^{ + [self.roomDataSource.room reportEvent:selectedEvent.eventId score:-100 reason:text success:^{ __strong __typeof(weakSelf)self = weakSelf; [self stopActivityIndicator]; @@ -2974,7 +2998,7 @@ { // Create the contact related to this member MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:mxMember.displayname andMatrixID:mxMember.userId]; - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:mxMember.userId]; + contactsDataSource.ignoredContactsByMatrixId[mxMember.userId] = contact; } } @@ -3080,7 +3104,7 @@ { __weak __typeof(self) weakSelf = self; - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; // Check app permissions first [MXKTools checkAccessForCall:video @@ -3327,13 +3351,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -3400,7 +3424,7 @@ [super scrollViewWillBeginDragging:scrollView]; } - if (self.expandedHeaderContainer.isHidden == NO) + if (!self.expandedHeaderContainer.isHidden) { // Store here the position of the first touch down event UIPanGestureRecognizer *panGestureRecognizer = scrollView.panGestureRecognizer; @@ -3728,7 +3752,7 @@ // keeps the only the first two users for(int i = 0; i < MIN(count, 2); i++) { - NSString* name = [currentTypingUsers objectAtIndex:i]; + NSString* name = currentTypingUsers[i]; MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:name]; @@ -3750,15 +3774,15 @@ } else if (1 == names.count) { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_one_user_is_typing", @"Vector", nil), [names objectAtIndex:0]]; + text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_one_user_is_typing", @"Vector", nil), names[0]]; } else if (2 == names.count) { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_two_users_are_typing", @"Vector", nil), [names objectAtIndex:0], [names objectAtIndex:1]]; + text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_two_users_are_typing", @"Vector", nil), names[0], names[1]]; } else { - text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_many_users_are_typing", @"Vector", nil), [names objectAtIndex:0], [names objectAtIndex:1]]; + text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_many_users_are_typing", @"Vector", nil), names[0], names[1]]; } [((RoomActivitiesView*) self.activitiesView) displayTypingNotification:text]; @@ -3985,7 +4009,7 @@ NSLog(@"[RoomVC] onOngoingConferenceCallPressed (jitsi)"); __weak __typeof(self) weakSelf = self; - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; // Check app permissions first [MXKTools checkAccessForCall:video @@ -4219,11 +4243,11 @@ // Set the right background color if (highlightCount) { - missedDiscussionsBadgeLabelBgView.backgroundColor = kRiotColorPinkRed; + missedDiscussionsBadgeLabelBgView.backgroundColor = ThemeService.shared.theme.noticeColor; } else { - missedDiscussionsBadgeLabelBgView.backgroundColor = kRiotColorGreen; + missedDiscussionsBadgeLabelBgView.backgroundColor = ThemeService.shared.theme.noticeSecondaryColor; } if (!missedDiscussionsButton || [leftBarButtonItems indexOfObject:missedDiscussionsButton] == NSNotFound) diff --git a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m index 94503a882..3ff3145d6 100644 --- a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m +++ b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m @@ -19,7 +19,8 @@ #import "RoomBubbleCellData.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXKRoomBubbleTableViewCell+Riot.h" @@ -68,7 +69,7 @@ if (cellData) { // Highlight the search pattern - [cellData highlightPatternInTextMessage:self.searchText withForegroundColor:kRiotColorGreen andFont:patternFont]; + [cellData highlightPatternInTextMessage:self.searchText withForegroundColor:ThemeService.shared.theme.tintColor andFont:patternFont]; // Use profile information as data to display MXSearchUserProfile *userProfile = result.context.profileInfo[result.result.sender]; diff --git a/Riot/Modules/Room/Search/Files/RoomFilesSearchViewController.m b/Riot/Modules/Room/Search/Files/RoomFilesSearchViewController.m index a710dc8cb..51c74d6a1 100644 --- a/Riot/Modules/Room/Search/Files/RoomFilesSearchViewController.m +++ b/Riot/Modules/Room/Search/Files/RoomFilesSearchViewController.m @@ -25,14 +25,15 @@ #import "FilesSearchTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface RoomFilesSearchViewController () { // Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar. id kAppDelegateDidTapStatusBarNotificationObserver; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -61,7 +62,7 @@ self.searchTableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -71,15 +72,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.searchTableView.backgroundColor; - self.noResultsLabel.textColor = kRiotPrimaryBgColor; + self.noResultsLabel.textColor = ThemeService.shared.theme.backgroundColor; if (self.searchTableView.dataSource) { @@ -89,17 +90,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -145,13 +146,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Room/Search/Messages/RoomMessagesSearchViewController.m b/Riot/Modules/Room/Search/Messages/RoomMessagesSearchViewController.m index f152865d4..a13185f45 100644 --- a/Riot/Modules/Room/Search/Messages/RoomMessagesSearchViewController.m +++ b/Riot/Modules/Room/Search/Messages/RoomMessagesSearchViewController.m @@ -27,14 +27,15 @@ #import "RoomIncomingTextMsgBubbleCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface RoomMessagesSearchViewController () { // Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar. id kAppDelegateDidTapStatusBarNotificationObserver; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -62,7 +63,7 @@ [self.searchTableView registerClass:RoomIncomingAttachmentBubbleCell.class forCellReuseIdentifier:RoomIncomingAttachmentBubbleCell.defaultReuseIdentifier]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -72,15 +73,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.searchTableView.backgroundColor; - self.noResultsLabel.textColor = kRiotPrimaryBgColor; + self.noResultsLabel.textColor = ThemeService.shared.theme.backgroundColor; if (self.searchTableView.dataSource) { @@ -90,17 +91,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -172,13 +173,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Room/Search/RoomSearchViewController.m b/Riot/Modules/Room/Search/RoomSearchViewController.m index 95f1e2015..674c4abc7 100644 --- a/Riot/Modules/Room/Search/RoomSearchViewController.m +++ b/Riot/Modules/Room/Search/RoomSearchViewController.m @@ -23,6 +23,7 @@ #import "FilesSearchCellData.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface RoomSearchViewController () { @@ -79,7 +80,7 @@ UIImageView *backgroundImageView = self.backgroundImageView; if (backgroundImageView) { - UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor]; + UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; backgroundImageView.image = image; } } diff --git a/Riot/Modules/Room/Settings/RoomSettingsViewController.m b/Riot/Modules/Room/Settings/RoomSettingsViewController.m index e3fcf752a..706a75f4f 100644 --- a/Riot/Modules/Room/Settings/RoomSettingsViewController.m +++ b/Riot/Modules/Room/Settings/RoomSettingsViewController.m @@ -29,6 +29,7 @@ #import "MXRoomSummary+Riot.h" #import "AppDelegate.h" +#import "Riot-Swift.h" #import "RoomMemberDetailsViewController.h" @@ -167,8 +168,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // A copy of the banned members NSArray *bannedMembers; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -255,7 +256,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [self setNavBarButtons]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -265,12 +266,12 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -281,7 +282,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -379,10 +380,10 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti actualDirectoryVisibilityRequest = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (appDelegateDidTapStatusBarNotificationObserver) @@ -713,9 +714,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti NSString *currentCanonicalAlias = mxRoomState.canonicalAlias; NSString *canonicalAlias; - if ([updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]) + if (updatedItemsDict[kRoomSettingsCanonicalAliasKey]) { - canonicalAlias = [updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]; + canonicalAlias = updatedItemsDict[kRoomSettingsCanonicalAliasKey]; } else { @@ -849,7 +850,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if (directoryVisibilitySwitch) { // Check a potential user's change before the end of the request - MXRoomDirectoryVisibility modifiedDirectoryVisibility = [updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]; + MXRoomDirectoryVisibility modifiedDirectoryVisibility = updatedItemsDict[kRoomSettingsDirectoryKey]; if (modifiedDirectoryVisibility) { if ([modifiedDirectoryVisibility isEqualToString:directoryVisibility]) @@ -882,7 +883,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Refresh here the related communities list. [relatedGroups removeAllObjects]; [relatedGroups addObjectsFromArray:mxRoomState.relatedGroups]; - NSArray *removedCommunities = [updatedItemsDict objectForKey:kRoomSettingsRemovedRelatedGroupKey]; + NSArray *removedCommunities = updatedItemsDict[kRoomSettingsRemovedRelatedGroupKey]; if (removedCommunities.count) { for (NSUInteger index = 0; index < relatedGroups.count;) @@ -900,7 +901,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } } } - NSArray *communities = [updatedItemsDict objectForKey:kRoomSettingsNewRelatedGroupKey]; + NSArray *communities = updatedItemsDict[kRoomSettingsNewRelatedGroupKey]; if (communities) { [relatedGroups addObjectsFromArray:communities]; @@ -947,7 +948,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Check whether the topic has been actually changed if ((topic || currentTopic) && ([topic isEqualToString:currentTopic] == NO)) { - [updatedItemsDict setObject:(topic ? topic : @"") forKey:kRoomSettingsTopicKey]; + updatedItemsDict[kRoomSettingsTopicKey] = topic ? topic : @""; } else { @@ -1098,7 +1099,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Check whether the name has been actually changed if ((displayName || currentName) && ([displayName isEqualToString:currentName] == NO)) { - [updatedItemsDict setObject:(displayName ? displayName : @"") forKey:kRoomSettingsNameKey]; + updatedItemsDict[kRoomSettingsNameKey] = displayName ? displayName : @""; } else { @@ -1187,10 +1188,10 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // check if there is some updates related to room state if (mxRoomState) { - if ([updatedItemsDict objectForKey:kRoomSettingsAvatarKey]) + if (updatedItemsDict[kRoomSettingsAvatarKey]) { // Retrieve the current picture and make sure its orientation is up - UIImage *updatedPicture = [MXKTools forceImageOrientationUp:[updatedItemsDict objectForKey:kRoomSettingsAvatarKey]]; + UIImage *updatedPicture = [MXKTools forceImageOrientationUp:updatedItemsDict[kRoomSettingsAvatarKey]]; // Upload picture uploader = [MXMediaManager prepareUploaderWithMatrixSession:mxRoom.mxSession initialRange:0 andRange:1.0]; @@ -1204,7 +1205,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti self->uploader = nil; [self->updatedItemsDict removeObjectForKey:kRoomSettingsAvatarKey]; - [self->updatedItemsDict setObject:url forKey:kRoomSettingsAvatarURLKey]; + self->updatedItemsDict[kRoomSettingsAvatarURLKey] = url; [self onSave:nil]; } @@ -1236,7 +1237,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } - NSString* photoUrl = [updatedItemsDict objectForKey:kRoomSettingsAvatarURLKey]; + NSString* photoUrl = updatedItemsDict[kRoomSettingsAvatarURLKey]; if (photoUrl) { pendingOperation = [mxRoom setAvatar:photoUrl success:^{ @@ -1278,7 +1279,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // has a new room name - NSString* roomName = [updatedItemsDict objectForKey:kRoomSettingsNameKey]; + NSString* roomName = updatedItemsDict[kRoomSettingsNameKey]; if (roomName) { pendingOperation = [mxRoom setName:roomName success:^{ @@ -1320,7 +1321,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // has a new room topic - NSString* roomTopic = [updatedItemsDict objectForKey:kRoomSettingsTopicKey]; + NSString* roomTopic = updatedItemsDict[kRoomSettingsTopicKey]; if (roomTopic) { pendingOperation = [mxRoom setTopic:roomTopic success:^{ @@ -1362,7 +1363,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room guest access - MXRoomGuestAccess guestAccess = [updatedItemsDict objectForKey:kRoomSettingsGuestAccessKey]; + MXRoomGuestAccess guestAccess = updatedItemsDict[kRoomSettingsGuestAccessKey]; if (guestAccess) { pendingOperation = [mxRoom setGuestAccess:guestAccess success:^{ @@ -1404,7 +1405,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room join rule - MXRoomJoinRule joinRule = [updatedItemsDict objectForKey:kRoomSettingsJoinRuleKey]; + MXRoomJoinRule joinRule = updatedItemsDict[kRoomSettingsJoinRuleKey]; if (joinRule) { pendingOperation = [mxRoom setJoinRule:joinRule success:^{ @@ -1446,7 +1447,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // History visibility - MXRoomHistoryVisibility visibility = [updatedItemsDict objectForKey:kRoomSettingsHistoryVisibilityKey]; + MXRoomHistoryVisibility visibility = updatedItemsDict[kRoomSettingsHistoryVisibilityKey]; if (visibility) { pendingOperation = [mxRoom setHistoryVisibility:visibility success:^{ @@ -1488,7 +1489,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room addresses - NSMutableArray *aliases = [updatedItemsDict objectForKey:kRoomSettingsNewAliasesKey]; + NSMutableArray *aliases = updatedItemsDict[kRoomSettingsNewAliasesKey]; if (aliases.count) { NSString *roomAlias = aliases.firstObject; @@ -1504,7 +1505,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if (aliases.count > 1) { [aliases removeObjectAtIndex:0]; - [self->updatedItemsDict setObject:aliases forKey:kRoomSettingsNewAliasesKey]; + self->updatedItemsDict[kRoomSettingsNewAliasesKey] = aliases; } else { @@ -1541,7 +1542,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } - aliases = [updatedItemsDict objectForKey:kRoomSettingsRemovedAliasesKey]; + aliases = updatedItemsDict[kRoomSettingsRemovedAliasesKey]; if (aliases.count) { NSString *roomAlias = aliases.firstObject; @@ -1557,7 +1558,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if (aliases.count > 1) { [aliases removeObjectAtIndex:0]; - [self->updatedItemsDict setObject:aliases forKey:kRoomSettingsRemovedAliasesKey]; + self->updatedItemsDict[kRoomSettingsRemovedAliasesKey] = aliases; } else { @@ -1594,7 +1595,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } - NSString* canonicalAlias = [updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]; + NSString* canonicalAlias = updatedItemsDict[kRoomSettingsCanonicalAliasKey]; if (canonicalAlias) { pendingOperation = [mxRoom setCanonicalAlias:canonicalAlias success:^{ @@ -1636,7 +1637,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Related groups - if ([updatedItemsDict objectForKey:kRoomSettingsNewRelatedGroupKey] || [updatedItemsDict objectForKey:kRoomSettingsRemovedRelatedGroupKey]) + if (updatedItemsDict[kRoomSettingsNewRelatedGroupKey] || updatedItemsDict[kRoomSettingsRemovedRelatedGroupKey]) { [self refreshRelatedGroups]; @@ -1683,7 +1684,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Update here other room settings - NSString *roomTag = [updatedItemsDict objectForKey:kRoomSettingsTagKey]; + NSString *roomTag = updatedItemsDict[kRoomSettingsTagKey]; if (roomTag) { if (!roomTag.length) @@ -1706,9 +1707,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } - if ([updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]) + if (updatedItemsDict[kRoomSettingsMuteNotifKey]) { - if (((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]).boolValue) + if (((NSNumber*) updatedItemsDict[kRoomSettingsMuteNotifKey]).boolValue) { [mxRoom mentionsOnly:^{ @@ -1739,9 +1740,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } - if ([updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]) + if (updatedItemsDict[kRoomSettingsDirectChatKey]) { - pendingOperation = [mxRoom setIsDirect:((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]).boolValue withUserId:nil success:^{ + pendingOperation = [mxRoom setIsDirect:((NSNumber*) updatedItemsDict[kRoomSettingsDirectChatKey]).boolValue withUserId:nil success:^{ if (weakSelf) { @@ -1752,7 +1753,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [self onSave:nil]; } - } failure:^(NSError *error) { + } failure:^(NSError *error) { NSLog(@"[RoomSettingsViewController] Altering DMness failed"); @@ -1779,7 +1780,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room directory visibility - MXRoomDirectoryVisibility directoryVisibility = [updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]; + MXRoomDirectoryVisibility directoryVisibility = updatedItemsDict[kRoomSettingsDirectoryKey]; if (directoryVisibility) { [mxRoom setDirectoryVisibility:directoryVisibility success:^{ @@ -1820,7 +1821,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room encryption - if ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey]) + if (updatedItemsDict[kRoomSettingsEncryptionKey]) { pendingOperation = [mxRoom enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{ @@ -1862,7 +1863,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } // Room settings on blacklist unverified devices - if ([updatedItemsDict objectForKey:kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) + if (updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) { BOOL blacklistUnverifiedDevices = [((NSNumber*)updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) boolValue]; [mxRoom.mxSession.crypto setBlacklistUnverifiedDevicesInRoom:mxRoom.roomId blacklist:blacklistUnverifiedDevices]; @@ -1884,7 +1885,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [roomAddresses removeAllObjects]; localAddressesCount = 0; - NSArray *removedAliases = [updatedItemsDict objectForKey:kRoomSettingsRemovedAliasesKey]; + NSArray *removedAliases = updatedItemsDict[kRoomSettingsRemovedAliasesKey]; NSArray *aliases = mxRoomState.aliases; if (aliases) @@ -1908,7 +1909,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } } - aliases = [updatedItemsDict objectForKey:kRoomSettingsNewAliasesKey]; + aliases = updatedItemsDict[kRoomSettingsNewAliasesKey]; for (NSString *alias in aliases) { // Add this new alias to local addresses @@ -1938,7 +1939,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti count = ROOM_SETTINGS_ROOM_ACCESS_SECTION_ROW_SUB_COUNT; // Check whether a room address is required for the current join rule - NSString *joinRule = [updatedItemsDict objectForKey:kRoomSettingsJoinRuleKey]; + NSString *joinRule = updatedItemsDict[kRoomSettingsJoinRuleKey]; if (!joinRule) { // Use the actual values if no change is pending. @@ -2052,7 +2053,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; - tableViewHeaderFooterView.textLabel.textColor = kRiotPrimaryTextColor; + tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; tableViewHeaderFooterView.textLabel.font = [UIFont systemFontOfSize:15]; } } @@ -2126,9 +2127,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomNotifCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_mute_notifs", @"Vector", nil); - if ([updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]) + if (updatedItemsDict[kRoomSettingsMuteNotifKey]) { - roomNotifCell.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]).boolValue; + roomNotifCell.mxkSwitch.on = ((NSNumber*) updatedItemsDict[kRoomSettingsMuteNotifKey]).boolValue; } else { @@ -2145,9 +2146,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomDirectChat.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_direct_chat", @"Vector", nil); - if ([updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]) + if (updatedItemsDict[kRoomSettingsDirectChatKey]) { - roomDirectChat.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]).boolValue; + roomDirectChat.mxkSwitch.on = ((NSNumber*) updatedItemsDict[kRoomSettingsDirectChatKey]).boolValue; } else { @@ -2177,11 +2178,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomPhotoCell.mxkImageView.defaultBackgroundColor = [UIColor clearColor]; roomPhotoCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_photo", @"Vector", nil); - roomPhotoCell.mxkLabel.textColor = kRiotPrimaryTextColor; + roomPhotoCell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - if ([updatedItemsDict objectForKey:kRoomSettingsAvatarKey]) + if (updatedItemsDict[kRoomSettingsAvatarKey]) { - roomPhotoCell.mxkImageView.image = (UIImage*)[updatedItemsDict objectForKey:kRoomSettingsAvatarKey]; + roomPhotoCell.mxkImageView.image = (UIImage*) updatedItemsDict[kRoomSettingsAvatarKey]; } else { @@ -2203,25 +2204,25 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti topicTextView = roomTopicCell.textView; - if ([updatedItemsDict objectForKey:kRoomSettingsTopicKey]) + if (updatedItemsDict[kRoomSettingsTopicKey]) { - topicTextView.text = (NSString*)[updatedItemsDict objectForKey:kRoomSettingsTopicKey]; + topicTextView.text = (NSString*) updatedItemsDict[kRoomSettingsTopicKey]; } else { topicTextView.text = mxRoomState.topic; } - topicTextView.tintColor = kRiotColorGreen; + topicTextView.tintColor = ThemeService.shared.theme.tintColor; topicTextView.font = [UIFont systemFontOfSize:15]; topicTextView.bounces = NO; topicTextView.delegate = self; // disable the edition if the user cannot update it topicTextView.editable = (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic]); - topicTextView.textColor = kRiotSecondaryTextColor; + topicTextView.textColor = ThemeService.shared.theme.textSecondaryColor; - topicTextView.keyboardAppearance = kRiotKeyboard; + topicTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance; cell = roomTopicCell; } @@ -2234,22 +2235,22 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomNameCell.mxkTextFieldTrailingConstraint.constant = 15; roomNameCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_room_name", @"Vector", nil); - roomNameCell.mxkLabel.textColor = kRiotPrimaryTextColor; + roomNameCell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; roomNameCell.accessoryType = UITableViewCellAccessoryNone; roomNameCell.accessoryView = nil; nameTextField = roomNameCell.mxkTextField; - nameTextField.tintColor = kRiotColorGreen; + nameTextField.tintColor = ThemeService.shared.theme.tintColor; nameTextField.font = [UIFont systemFontOfSize:17]; nameTextField.borderStyle = UITextBorderStyleNone; nameTextField.textAlignment = NSTextAlignmentRight; nameTextField.delegate = self; - if ([updatedItemsDict objectForKey:kRoomSettingsNameKey]) + if (updatedItemsDict[kRoomSettingsNameKey]) { - nameTextField.text = (NSString*)[updatedItemsDict objectForKey:kRoomSettingsNameKey]; + nameTextField.text = (NSString*) updatedItemsDict[kRoomSettingsNameKey]; } else { @@ -2258,7 +2259,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // disable the edition if the user cannot update it nameTextField.userInteractionEnabled = (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName]); - nameTextField.textColor = kRiotSecondaryTextColor; + nameTextField.textColor = ThemeService.shared.theme.textSecondaryColor; // Add a "textFieldDidChange" notification method to the text field control. [nameTextField addTarget:self action:@selector(onTextFieldUpdate:) forControlEvents:UIControlEventEditingChanged]; @@ -2279,15 +2280,15 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti NSArray *labels = roomTagCell.labels; UILabel *label; label = labels[0]; - label.textColor = kRiotPrimaryTextColor; + label.textColor = ThemeService.shared.theme.textPrimaryColor; label.text = NSLocalizedStringFromTable(@"room_details_favourite_tag", @"Vector", nil); label = labels[1]; - label.textColor = kRiotPrimaryTextColor; + label.textColor = ThemeService.shared.theme.textPrimaryColor; label.text = NSLocalizedStringFromTable(@"room_details_low_priority_tag", @"Vector", nil); - if ([updatedItemsDict objectForKey:kRoomSettingsTagKey]) + if (updatedItemsDict[kRoomSettingsTagKey]) { - NSString *roomTag = [updatedItemsDict objectForKey:kRoomSettingsTagKey]; + NSString *roomTag = updatedItemsDict[kRoomSettingsTagKey]; if ([roomTag isEqualToString:kMXRoomTagFavourite]) { [roomTagCell setCheckBoxValue:YES atIndex:0]; @@ -2319,7 +2320,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [leaveCell.mxkButton setTitle:title forState:UIControlStateNormal]; [leaveCell.mxkButton setTitle:title forState:UIControlStateHighlighted]; - [leaveCell.mxkButton setTintColor:kRiotColorGreen]; + [leaveCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; leaveCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [leaveCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2338,9 +2339,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [directoryToggleCell.mxkSwitch addTarget:self action:@selector(toggleDirectoryVisibility:) forControlEvents:UIControlEventValueChanged]; - if ([updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]) + if (updatedItemsDict[kRoomSettingsDirectoryKey]) { - directoryToggleCell.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]).boolValue; + directoryToggleCell.mxkSwitch.on = ((NSNumber*) updatedItemsDict[kRoomSettingsDirectoryKey]).boolValue; } else { @@ -2361,7 +2362,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti cell = [tableView dequeueReusableCellWithIdentifier:kRoomSettingsWarningCellViewIdentifier forIndexPath:indexPath]; cell.textLabel.font = [UIFont systemFontOfSize:17]; - cell.textLabel.textColor = kRiotColorPinkRed; + cell.textLabel.textColor = ThemeService.shared.theme.warningColor; cell.textLabel.numberOfLines = 0; cell.accessoryView = nil; cell.accessoryType = UITableViewCellAccessoryNone; @@ -2375,8 +2376,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomAccessCell.checkBoxLeadingConstraint.constant = roomAccessCell.separatorInset.left; // Retrieve the potential updated values for joinRule and guestAccess - NSString *joinRule = [updatedItemsDict objectForKey:kRoomSettingsJoinRuleKey]; - NSString *guestAccess = [updatedItemsDict objectForKey:kRoomSettingsGuestAccessKey]; + NSString *joinRule = updatedItemsDict[kRoomSettingsJoinRuleKey]; + NSString *guestAccess = updatedItemsDict[kRoomSettingsGuestAccessKey]; // Use the actual values if no change is pending if (!joinRule) @@ -2427,7 +2428,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti historyVisibilityCell.checkBoxLeadingConstraint.constant = historyVisibilityCell.separatorInset.left; // Retrieve first the potential updated value for history visibility - NSString *visibility = [updatedItemsDict objectForKey:kRoomSettingsHistoryVisibilityKey]; + NSString *visibility = updatedItemsDict[kRoomSettingsHistoryVisibilityKey]; // Use the actual value if no change is pending if (!visibility) @@ -2442,7 +2443,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti historyVisibilityCell.enabled = ([visibility isEqualToString:kMXRoomHistoryVisibilityWorldReadable]); - [historyVisibilityTickCells setObject:historyVisibilityCell forKey:kMXRoomHistoryVisibilityWorldReadable]; + historyVisibilityTickCells[kMXRoomHistoryVisibilityWorldReadable] = historyVisibilityCell; } else if (indexPath.row == ROOM_SETTINGS_HISTORY_VISIBILITY_SECTION_ROW_MEMBERS_ONLY) { @@ -2451,7 +2452,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti historyVisibilityCell.enabled = ([visibility isEqualToString:kMXRoomHistoryVisibilityShared]); - [historyVisibilityTickCells setObject:historyVisibilityCell forKey:kMXRoomHistoryVisibilityShared]; + historyVisibilityTickCells[kMXRoomHistoryVisibilityShared] = historyVisibilityCell; } else if (indexPath.row == ROOM_SETTINGS_HISTORY_VISIBILITY_SECTION_ROW_MEMBERS_ONLY_SINCE_INVITED) { @@ -2460,7 +2461,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti historyVisibilityCell.enabled = ([visibility isEqualToString:kMXRoomHistoryVisibilityInvited]); - [historyVisibilityTickCells setObject:historyVisibilityCell forKey:kMXRoomHistoryVisibilityInvited]; + historyVisibilityTickCells[kMXRoomHistoryVisibilityInvited] = historyVisibilityCell; } else if (indexPath.row == ROOM_SETTINGS_HISTORY_VISIBILITY_SECTION_ROW_MEMBERS_ONLY_SINCE_JOINED) { @@ -2469,7 +2470,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti historyVisibilityCell.enabled = ([visibility isEqualToString:kMXRoomHistoryVisibilityJoined]); - [historyVisibilityTickCells setObject:historyVisibilityCell forKey:kMXRoomHistoryVisibilityJoined]; + historyVisibilityTickCells[kMXRoomHistoryVisibilityJoined] = historyVisibilityCell; } // Check whether the user can change this option @@ -2498,17 +2499,14 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti addAddressTextField = addAddressCell.mxkTextField; addAddressTextField.placeholder = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_details_new_address_placeholder", @"Vector", nil), self.mainSession.matrixRestClient.homeserverSuffix]; - if (kRiotPlaceholderTextColor) - { - addAddressTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:addAddressTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + addAddressTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:addAddressTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; addAddressTextField.userInteractionEnabled = YES; addAddressTextField.text = currentValue; - addAddressTextField.textColor = kRiotSecondaryTextColor; + addAddressTextField.textColor = ThemeService.shared.theme.textSecondaryColor; - addAddressTextField.tintColor = kRiotColorGreen; + addAddressTextField.tintColor = ThemeService.shared.theme.tintColor; addAddressTextField.font = [UIFont systemFontOfSize:17]; addAddressTextField.borderStyle = UITextBorderStyleNone; addAddressTextField.textAlignment = NSTextAlignmentLeft; @@ -2524,7 +2522,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti UITableViewCell *addressCell = [tableView dequeueReusableCellWithIdentifier:kRoomSettingsAddressCellViewIdentifier forIndexPath:indexPath]; addressCell.textLabel.font = [UIFont systemFontOfSize:16]; - addressCell.textLabel.textColor = kRiotPrimaryTextColor; + addressCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; addressCell.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; addressCell.accessoryView = nil; addressCell.accessoryType = UITableViewCellAccessoryNone; @@ -2544,9 +2542,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti NSString *alias = roomAddresses[row]; NSString *canonicalAlias; - if ([updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]) + if (updatedItemsDict[kRoomSettingsCanonicalAliasKey]) { - canonicalAlias = [updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]; + canonicalAlias = updatedItemsDict[kRoomSettingsCanonicalAliasKey]; } else { @@ -2589,17 +2587,14 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti addGroupTextField = addCommunityCell.mxkTextField; addGroupTextField.placeholder = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_details_new_flair_placeholder", @"Vector", nil), self.mainSession.matrixRestClient.homeserverSuffix]; - if (kRiotPlaceholderTextColor) - { - addGroupTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:addGroupTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + addGroupTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:addGroupTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; addGroupTextField.userInteractionEnabled = YES; addGroupTextField.text = currentValue; - addGroupTextField.textColor = kRiotSecondaryTextColor; + addGroupTextField.textColor = ThemeService.shared.theme.textSecondaryColor; - addGroupTextField.tintColor = kRiotColorGreen; + addGroupTextField.tintColor = ThemeService.shared.theme.tintColor; addGroupTextField.font = [UIFont systemFontOfSize:17]; addGroupTextField.borderStyle = UITextBorderStyleNone; addGroupTextField.textAlignment = NSTextAlignmentLeft; @@ -2615,7 +2610,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti UITableViewCell *communityCell = [tableView dequeueReusableCellWithIdentifier:kRoomSettingsAddressCellViewIdentifier forIndexPath:indexPath]; communityCell.textLabel.font = [UIFont systemFontOfSize:16]; - communityCell.textLabel.textColor = kRiotPrimaryTextColor; + communityCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; communityCell.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; communityCell.accessoryView = nil; communityCell.accessoryType = UITableViewCellAccessoryNone; @@ -2633,7 +2628,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti UITableViewCell *addressCell = [tableView dequeueReusableCellWithIdentifier:kRoomSettingsAddressCellViewIdentifier forIndexPath:indexPath]; addressCell.textLabel.font = [UIFont systemFontOfSize:16]; - addressCell.textLabel.textColor = kRiotPrimaryTextColor; + addressCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; addressCell.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; addressCell.accessoryView = nil; addressCell.accessoryType = UITableViewCellAccessoryNone; @@ -2655,11 +2650,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti cell.textLabel.font = [UIFont systemFontOfSize:17]; cell.textLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_room_id", @"Vector", nil); - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.detailTextLabel.font = [UIFont systemFontOfSize:15]; cell.detailTextLabel.text = mxRoomState.roomId; - cell.detailTextLabel.textColor = kRiotSecondaryTextColor; + cell.detailTextLabel.textColor = ThemeService.shared.theme.textSecondaryColor; cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; cell.selectionStyle = UITableViewCellSelectionStyleNone; @@ -2671,7 +2666,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti MXKTableViewCellWithLabelAndSwitch *roomBlacklistUnverifiedDevicesCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; [roomBlacklistUnverifiedDevicesCell.mxkSwitch addTarget:self action:@selector(toggleBlacklistUnverifiedDevice:) forControlEvents:UIControlEventValueChanged]; - roomBlacklistUnverifiedDevicesCell.mxkSwitch.onTintColor = kRiotColorGreen; + roomBlacklistUnverifiedDevicesCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; roomBlacklistUnverifiedDevicesCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_blacklist_unverified_devices", @"Vector", nil); @@ -2690,7 +2685,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { roomBlacklistUnverifiedDevicesCell.mxkSwitch.enabled = YES; - if ([updatedItemsDict objectForKey:kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) + if (updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) { blacklistUnverifiedDevices = [((NSNumber*)updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) boolValue]; } @@ -2718,7 +2713,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti cell.textLabel.font = [UIFont systemFontOfSize:17]; cell.textLabel.numberOfLines = 0; cell.textLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_enabled", @"Vector", nil); - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.selectionStyle = UITableViewCellSelectionStyleNone; } @@ -2737,7 +2732,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomEncryptionCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_enable_e2e_encryption", @"Vector", nil); - roomEncryptionCell.mxkSwitch.on = ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey] != nil); + roomEncryptionCell.mxkSwitch.on = (updatedItemsDict[kRoomSettingsEncryptionKey] != nil); cell = roomEncryptionCell; } @@ -2752,7 +2747,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti cell.textLabel.font = [UIFont systemFontOfSize:17]; cell.textLabel.numberOfLines = 0; cell.textLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_disabled", @"Vector", nil); - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.selectionStyle = UITableViewCellSelectionStyleNone; } @@ -2800,9 +2795,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left; cell.mxkSwitchTrailingConstraint.constant = 15; - cell.mxkLabel.textColor = kRiotPrimaryTextColor; + cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - cell.mxkSwitch.onTintColor = kRiotColorGreen; + cell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; [cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventValueChanged]; // Reset the stored `directoryVisibilitySwitch` if the corresponding cell is reused. @@ -2821,13 +2816,13 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -2886,7 +2881,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomJoinRuleInvite forKey:kRoomSettingsJoinRuleKey]; + updatedItemsDict[kRoomSettingsJoinRuleKey] = kMXRoomJoinRuleInvite; // Update guest access to allow guest on invitation. // Note: if guest_access is "forbidden" here, guests cannot join this room even if explicitly invited. @@ -2896,7 +2891,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomGuestAccessCanJoin forKey:kRoomSettingsGuestAccessKey]; + updatedItemsDict[kRoomSettingsGuestAccessKey] = kMXRoomGuestAccessCanJoin; } } @@ -2929,7 +2924,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomJoinRulePublic forKey:kRoomSettingsJoinRuleKey]; + updatedItemsDict[kRoomSettingsJoinRuleKey] = kMXRoomJoinRulePublic; } if ([mxRoomState.guestAccess isEqualToString:kMXRoomGuestAccessForbidden]) @@ -2938,7 +2933,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomGuestAccessForbidden forKey:kRoomSettingsGuestAccessKey]; + updatedItemsDict[kRoomSettingsGuestAccessKey] = kMXRoomGuestAccessForbidden; } } @@ -2971,7 +2966,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomJoinRulePublic forKey:kRoomSettingsJoinRuleKey]; + updatedItemsDict[kRoomSettingsJoinRuleKey] = kMXRoomJoinRulePublic; } if ([mxRoomState.guestAccess isEqualToString:kMXRoomGuestAccessCanJoin]) @@ -2980,7 +2975,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:kMXRoomGuestAccessCanJoin forKey:kRoomSettingsGuestAccessKey]; + updatedItemsDict[kRoomSettingsGuestAccessKey] = kMXRoomGuestAccessCanJoin; } } @@ -3116,7 +3111,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti }]; - removeAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(44, 44) resourceSize:CGSizeMake(24, 24)]; + removeAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(44, 44) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:removeAction atIndex:0]; } } @@ -3131,7 +3126,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti }]; - removeAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(44, 44) resourceSize:CGSizeMake(24, 24)]; + removeAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(44, 44) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:removeAction atIndex:0]; } @@ -3201,7 +3196,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:historyVisibility forKey:kRoomSettingsHistoryVisibilityKey]; + updatedItemsDict[kRoomSettingsHistoryVisibilityKey] = historyVisibility; } [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); @@ -3259,7 +3254,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Remove the canonical address if (self->mxRoomState.canonicalAlias.length) { - [self->updatedItemsDict setObject:@"" forKey:kRoomSettingsCanonicalAliasKey]; + self->updatedItemsDict[kRoomSettingsCanonicalAliasKey] = @""; } else { @@ -3305,7 +3300,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { [self getNavigationItem].rightBarButtonItem.enabled = YES; - [updatedItemsDict setObject:image forKey:kRoomSettingsAvatarKey]; + updatedItemsDict[kRoomSettingsAvatarKey] = image; [self refreshRoomSettings]; } @@ -3401,7 +3396,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:[NSNumber numberWithBool:theSwitch.on] forKey:kRoomSettingsMuteNotifKey]; + updatedItemsDict[kRoomSettingsMuteNotifKey] = @(theSwitch.on); } [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); @@ -3415,7 +3410,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:[NSNumber numberWithBool:theSwitch.on] forKey:kRoomSettingsDirectChatKey]; + updatedItemsDict[kRoomSettingsDirectChatKey] = @(theSwitch.on); } [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); @@ -3458,7 +3453,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti typeof(self) self = weakSelf; self->currentAlert = nil; - [self->updatedItemsDict setObject:@(YES) forKey:kRoomSettingsEncryptionKey]; + self->updatedItemsDict[kRoomSettingsEncryptionKey] = @(YES); [self getNavigationItem].rightBarButtonItem.enabled = self->updatedItemsDict.count; } @@ -3504,12 +3499,12 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:visibility forKey:kRoomSettingsDirectoryKey]; + updatedItemsDict[kRoomSettingsDirectoryKey] = visibility; } } else { - [updatedItemsDict setObject:visibility forKey:kRoomSettingsDirectoryKey]; + updatedItemsDict[kRoomSettingsDirectoryKey] = visibility; } [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); @@ -3526,7 +3521,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:alias forKey:kRoomSettingsCanonicalAliasKey]; + updatedItemsDict[kRoomSettingsCanonicalAliasKey] = alias; } NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:ROOM_SETTINGS_ROOM_ADDRESSES_SECTION_INDEX]; @@ -3559,9 +3554,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { NSString *canonicalAlias; - if ([updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]) + if (updatedItemsDict[kRoomSettingsCanonicalAliasKey]) { - canonicalAlias = [updatedItemsDict objectForKey:kRoomSettingsCanonicalAliasKey]; + canonicalAlias = updatedItemsDict[kRoomSettingsCanonicalAliasKey]; } else { @@ -3582,7 +3577,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti else { // Check whether the alias has just been added - NSMutableArray *addedAlias = [updatedItemsDict objectForKey:kRoomSettingsNewAliasesKey]; + NSMutableArray *addedAlias = updatedItemsDict[kRoomSettingsNewAliasesKey]; if (addedAlias && [addedAlias indexOfObject:roomAlias] != NSNotFound) { [addedAlias removeObject:roomAlias]; @@ -3594,11 +3589,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - NSMutableArray *removedAlias = [updatedItemsDict objectForKey:kRoomSettingsRemovedAliasesKey]; + NSMutableArray *removedAlias = updatedItemsDict[kRoomSettingsRemovedAliasesKey]; if (!removedAlias) { removedAlias = [NSMutableArray array]; - [updatedItemsDict setObject:removedAlias forKey:kRoomSettingsRemovedAliasesKey]; + updatedItemsDict[kRoomSettingsRemovedAliasesKey] = removedAlias; } [removedAlias addObject:roomAlias]; @@ -3622,7 +3617,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti - (void)removeCommunity:(NSString*)groupId { // Check whether the alias has just been added - NSMutableArray *addedGroup = [updatedItemsDict objectForKey:kRoomSettingsNewRelatedGroupKey]; + NSMutableArray *addedGroup = updatedItemsDict[kRoomSettingsNewRelatedGroupKey]; if (addedGroup && [addedGroup indexOfObject:groupId] != NSNotFound) { [addedGroup removeObject:groupId]; @@ -3634,11 +3629,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - NSMutableArray *removedGroup = [updatedItemsDict objectForKey:kRoomSettingsRemovedRelatedGroupKey]; + NSMutableArray *removedGroup = updatedItemsDict[kRoomSettingsRemovedRelatedGroupKey]; if (!removedGroup) { removedGroup = [NSMutableArray array]; - [updatedItemsDict setObject:removedGroup forKey:kRoomSettingsRemovedRelatedGroupKey]; + updatedItemsDict[kRoomSettingsRemovedRelatedGroupKey] = removedGroup; } [removedGroup addObject:groupId]; @@ -3656,7 +3651,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if ([MXTools isMatrixRoomAlias:roomAlias]) { // Check whether this alias has just been deleted - NSMutableArray *removedAlias = [updatedItemsDict objectForKey:kRoomSettingsRemovedAliasesKey]; + NSMutableArray *removedAlias = updatedItemsDict[kRoomSettingsRemovedAliasesKey]; if (removedAlias && [removedAlias indexOfObject:roomAlias] != NSNotFound) { [removedAlias removeObject:roomAlias]; @@ -3669,11 +3664,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Check whether this alias is not already defined for this room else if ([roomAddresses indexOfObject:roomAlias] == NSNotFound) { - NSMutableArray *addedAlias = [updatedItemsDict objectForKey:kRoomSettingsNewAliasesKey]; + NSMutableArray *addedAlias = updatedItemsDict[kRoomSettingsNewAliasesKey]; if (!addedAlias) { addedAlias = [NSMutableArray array]; - [updatedItemsDict setObject:addedAlias forKey:kRoomSettingsNewAliasesKey]; + updatedItemsDict[kRoomSettingsNewAliasesKey] = addedAlias; } [addedAlias addObject:roomAlias]; @@ -3693,7 +3688,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:roomAlias forKey:kRoomSettingsCanonicalAliasKey]; + updatedItemsDict[kRoomSettingsCanonicalAliasKey] = roomAlias; } if (missingAddressWarningIndex != -1) @@ -3746,7 +3741,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if ([MXTools isMatrixGroupIdentifier:groupId]) { // Check whether this group has just been deleted - NSMutableArray *removedGroups = [updatedItemsDict objectForKey:kRoomSettingsRemovedRelatedGroupKey]; + NSMutableArray *removedGroups = updatedItemsDict[kRoomSettingsRemovedRelatedGroupKey]; if (removedGroups && [removedGroups indexOfObject:groupId] != NSNotFound) { [removedGroups removeObject:groupId]; @@ -3759,11 +3754,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Check whether this alias is not already defined for this room else if ([relatedGroups indexOfObject:groupId] == NSNotFound) { - NSMutableArray *addedGroup = [updatedItemsDict objectForKey:kRoomSettingsNewRelatedGroupKey]; + NSMutableArray *addedGroup = updatedItemsDict[kRoomSettingsNewRelatedGroupKey]; if (!addedGroup) { addedGroup = [NSMutableArray array]; - [updatedItemsDict setObject:addedGroup forKey:kRoomSettingsNewRelatedGroupKey]; + updatedItemsDict[kRoomSettingsNewRelatedGroupKey] = addedGroup; } [addedGroup addObject:groupId]; @@ -3819,7 +3814,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { // The user wants to unselect this tag // Retrieve the current change on room tag (if any) - NSString *updatedRoomTag = [updatedItemsDict objectForKey:kRoomSettingsTagKey]; + NSString *updatedRoomTag = updatedItemsDict[kRoomSettingsTagKey]; // Check the actual tag on mxRoom if (mxRoom.accountData.tags[tappedRoomTag]) @@ -3827,7 +3822,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // The actual tag must be updated, check whether another tag is already set if (!updatedRoomTag) { - [updatedItemsDict setObject:@"" forKey:kRoomSettingsTagKey]; + updatedItemsDict[kRoomSettingsTagKey] = @""; } } else if (updatedRoomTag && [updatedRoomTag isEqualToString:tappedRoomTag]) @@ -3835,7 +3830,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Cancel the updated tag, but take into account the cancellation of another tag when 'tappedRoomTag' was selected. if (mxRoom.accountData.tags.count) { - [updatedItemsDict setObject:@"" forKey:kRoomSettingsTagKey]; + updatedItemsDict[kRoomSettingsTagKey] = @""; } else { @@ -3856,7 +3851,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [updatedItemsDict setObject:tappedRoomTag forKey:kRoomSettingsTagKey]; + updatedItemsDict[kRoomSettingsTagKey] = tappedRoomTag; } // Select the tapped tag diff --git a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m index e93bf1440..5f575c745 100644 --- a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m +++ b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m @@ -17,7 +17,8 @@ #import "TableViewCellWithCheckBoxAndLabel.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation TableViewCellWithCheckBoxAndLabel @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - _label.textColor = kRiotPrimaryTextColor; + _label.textColor = ThemeService.shared.theme.textPrimaryColor; } - (void)setEnabled:(BOOL)enabled diff --git a/Riot/Modules/Room/Settings/Views/TableViewCellWithLabelAndLargeTextView.m b/Riot/Modules/Room/Settings/Views/TableViewCellWithLabelAndLargeTextView.m index 4260ad0f1..deecdd99e 100644 --- a/Riot/Modules/Room/Settings/Views/TableViewCellWithLabelAndLargeTextView.m +++ b/Riot/Modules/Room/Settings/Views/TableViewCellWithLabelAndLargeTextView.m @@ -17,7 +17,8 @@ #import "TableViewCellWithLabelAndLargeTextView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation TableViewCellWithLabelAndLargeTextView @@ -36,8 +37,8 @@ { [super customizeTableViewCellRendering]; - _label.textColor = kRiotPrimaryTextColor; - _textView.textColor = kRiotPrimaryTextColor; + _label.textColor = ThemeService.shared.theme.textPrimaryColor; + _textView.textColor = ThemeService.shared.theme.textPrimaryColor; } - (void)layoutSubviews diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m index 586f572af..e5b959ec4 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m @@ -17,7 +17,8 @@ #import "RoomActivitiesView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import @@ -111,10 +112,10 @@ { [super customizeViewRendering]; - self.separatorView.backgroundColor = kRiotSecondaryBgColor; - if (self.messageLabel.textColor != kRiotColorPinkRed) + self.separatorView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + if (self.messageLabel.textColor != ThemeService.shared.theme.warningColor) { - self.messageLabel.textColor = kRiotSecondaryTextColor; + self.messageLabel.textColor = ThemeService.shared.theme.textSecondaryColor; } } @@ -144,18 +145,18 @@ [tappableNotif addAttribute:NSLinkAttributeName value:@"onCancelLink" range:range]; NSRange wholeString = NSMakeRange(0, tappableNotif.length); - [tappableNotif addAttribute:NSForegroundColorAttributeName value:kRiotColorPinkRed range:wholeString]; + [tappableNotif addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.warningColor range:wholeString]; [tappableNotif addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:15] range:wholeString]; self.messageTextView.attributedText = tappableNotif; - self.messageTextView.tintColor = kRiotColorPinkRed; + self.messageTextView.tintColor = ThemeService.shared.theme.warningColor; self.messageTextView.hidden = NO; self.messageTextView.backgroundColor = [UIColor clearColor]; } else { self.messageLabel.text = notification; - self.messageLabel.textColor = kRiotColorPinkRed; + self.messageLabel.textColor = ThemeService.shared.theme.warningColor; self.messageLabel.hidden = NO; } @@ -186,7 +187,7 @@ { self.iconImageView.image = [UIImage imageNamed:@"error"]; self.messageLabel.text = labelText; - self.messageLabel.textColor = kRiotColorPinkRed; + self.messageLabel.textColor = ThemeService.shared.theme.warningColor; self.iconImageView.hidden = NO; self.messageLabel.hidden = NO; @@ -259,16 +260,16 @@ // Display the string in white on pink red NSRange wholeString = NSMakeRange(0, onGoingConferenceCallAttibutedString.length); - [onGoingConferenceCallAttibutedString addAttribute:NSForegroundColorAttributeName value:kRiotPrimaryBgColor range:wholeString]; - [onGoingConferenceCallAttibutedString addAttribute:NSBackgroundColorAttributeName value:kRiotColorPinkRed range:wholeString]; + [onGoingConferenceCallAttibutedString addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.backgroundColor range:wholeString]; + [onGoingConferenceCallAttibutedString addAttribute:NSBackgroundColorAttributeName value:ThemeService.shared.theme.warningColor range:wholeString]; [onGoingConferenceCallAttibutedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:15] range:wholeString]; self.messageTextView.attributedText = onGoingConferenceCallAttibutedString; - self.messageTextView.tintColor = kRiotPrimaryBgColor; + self.messageTextView.tintColor = ThemeService.shared.theme.backgroundColor; self.messageTextView.hidden = NO; - self.backgroundColor = kRiotColorPinkRed; - self.messageTextView.backgroundColor = kRiotColorPinkRed; + self.backgroundColor = ThemeService.shared.theme.warningColor; + self.messageTextView.backgroundColor = ThemeService.shared.theme.warningColor; // Hide the separator to display correctly the red pink conf call banner self.separatorView.hidden = YES; @@ -294,7 +295,7 @@ notification = NSLocalizedStringFromTable(@"room_new_message_notification", @"Vector", nil); } self.messageLabel.text = [NSString stringWithFormat:notification, newMessagesCount]; - self.messageLabel.textColor = kRiotColorPinkRed; + self.messageLabel.textColor = ThemeService.shared.theme.warningColor; self.messageLabel.hidden = NO; } else @@ -355,7 +356,7 @@ [roomReplacementAttributedString appendAttributedString:roomLinkAttributedString]; NSRange wholeStringRange = NSMakeRange(0, roomReplacementAttributedString.length); - [roomReplacementAttributedString addAttribute:NSForegroundColorAttributeName value:kRiotPrimaryTextColor range:wholeStringRange]; + [roomReplacementAttributedString addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.textPrimaryColor range:wholeStringRange]; self.messageTextView.attributedText = roomReplacementAttributedString; } @@ -364,7 +365,7 @@ self.messageTextView.text = NSLocalizedStringFromTable(@"room_replacement_information", @"Vector", nil); } - self.messageTextView.tintColor = kRiotPrimaryTextColor; + self.messageTextView.tintColor = ThemeService.shared.theme.textPrimaryColor; self.messageTextView.hidden = NO; self.messageTextView.backgroundColor = [UIColor clearColor]; @@ -431,13 +432,13 @@ message2 = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_resource_usage_limit_reached_message_2", @"Vector", nil) attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:fontSize], - NSForegroundColorAttributeName: kRiotPrimaryBgColor + NSForegroundColorAttributeName: ThemeService.shared.theme.backgroundColor }]; } NSDictionary *attributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:fontSize], - NSForegroundColorAttributeName: kRiotPrimaryBgColor + NSForegroundColorAttributeName: ThemeService.shared.theme.backgroundColor }; NSDictionary *messageContact2LinkAttributes; @@ -481,18 +482,18 @@ [attributedText appendAttributedString:messageContact3]; self.messageTextView.attributedText = attributedText; - self.messageTextView.tintColor = kRiotPrimaryBgColor; + self.messageTextView.tintColor = ThemeService.shared.theme.backgroundColor; self.messageTextView.hidden = NO; if (hardLimit) { - self.backgroundColor = kRiotColorPinkRed; - self.messageTextView.backgroundColor = kRiotColorPinkRed; + self.backgroundColor = ThemeService.shared.theme.warningColor; + self.messageTextView.backgroundColor = ThemeService.shared.theme.warningColor; } else { - self.backgroundColor = kRiotColorCuriousBlue; - self.messageTextView.backgroundColor = kRiotColorCuriousBlue; + self.backgroundColor = ThemeService.shared.riotColorCuriousBlue; + self.messageTextView.backgroundColor = ThemeService.shared.riotColorCuriousBlue; } // Hide the separator to display correctly the banner @@ -534,7 +535,7 @@ [self.messageTextView resignFirstResponder]; self.messageTextView.hidden = YES; - self.messageLabel.textColor = kRiotSecondaryTextColor; + self.messageLabel.textColor = ThemeService.shared.theme.textSecondaryColor; objc_removeAssociatedObjects(self.messageTextView); } diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m index 8439e4ccd..af76485f5 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m @@ -18,7 +18,7 @@ #import "RoomEncryptedDataBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" @implementation RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m index f390f2146..ba01ef0b5 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingAttachmentBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingAttachmentBubbleCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m index d405bf245..8686be688 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingAttachmentWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingAttachmentWithPaginationTitleBubbleCell @@ -25,11 +26,11 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; - self.messageTextView.tintColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m index d3e984383..712385152 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingAttachmentWithoutSenderInfoBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgBubbleCell.m index 0a09a63ca..605d9e9a3 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingTextMsgBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingTextMsgBubbleCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m index 6f85dded9..83bff1cf7 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingTextMsgWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingTextMsgWithPaginationTitleBubbleCell @@ -25,11 +26,11 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; - self.messageTextView.tintColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m index 02085a8b0..5078a9405 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m index 7a2544891..56b0db94c 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingTextMsgWithoutSenderInfoBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m index 2be960f3d..15ab0389e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomIncomingTextMsgWithoutSenderNameBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomIncomingTextMsgWithoutSenderNameBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipBubbleCell.m index 5073402c3..a15817d01 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipBubbleCell.m @@ -16,7 +16,8 @@ #import "RoomMembershipBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -40,7 +41,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)prepareForReuse diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedBubbleCell.m index 0c6bd06e4..c19773bf9 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomMembershipCollapsedBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -29,7 +30,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)layoutSubviews diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m index bf9c0c1ff..b93c8be3a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m @@ -16,7 +16,8 @@ #import "RoomMembershipCollapsedWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -26,8 +27,8 @@ { [super customizeTableViewCellRendering]; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedBubbleCell.m index 7803ec5db..8cf27a17f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedBubbleCell.m @@ -16,7 +16,8 @@ #import "RoomMembershipExpandedBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -37,9 +38,9 @@ NSString *const kRoomMembershipExpandedBubbleCellTapOnCollapseButton = @"kRoomMe { [super customizeTableViewCellRendering]; - self.separatorView.backgroundColor = kRiotSecondaryBgColor; + self.separatorView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - [self.collapseButton setTintColor:kRiotColorGreen]; + [self.collapseButton setTintColor:ThemeService.shared.theme.tintColor]; self.collapseButton.titleLabel.font = [UIFont systemFontOfSize:14]; } diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedWithPaginationTitleBubbleCell.m index 7efc5030c..63262f645 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipExpandedWithPaginationTitleBubbleCell.m @@ -16,7 +16,8 @@ #import "RoomMembershipExpandedWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -26,8 +27,8 @@ { [super customizeTableViewCellRendering]; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipWithPaginationTitleBubbleCell.m index 7c281d3e4..4ebebc70d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomMembershipWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomMembershipWithPaginationTitleBubbleCell.m @@ -16,7 +16,8 @@ #import "RoomMembershipWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "RoomBubbleCellData.h" @@ -26,8 +27,8 @@ { [super customizeTableViewCellRendering]; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m index d5e7d5cfa..bb786416a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingAttachmentBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingAttachmentBubbleCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData @@ -48,7 +49,7 @@ // Show a red border when the attachment sending failed if (bubbleCell->bubbleData.attachment.eventSentState == MXEventSentStateFailed) { - bubbleCell.attachmentView.layer.borderColor = kRiotColorPinkRed.CGColor; + bubbleCell.attachmentView.layer.borderColor = ThemeService.shared.theme.warningColor.CGColor; bubbleCell.attachmentView.layer.borderWidth = 1; } else diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m index 9ec7644e1..25911ddf2 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingAttachmentWithPaginationTitleBubbleCell @@ -25,11 +26,11 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; - self.messageTextView.tintColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m index a106baa47..b60dac677 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingAttachmentWithoutSenderInfoBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgBubbleCell.m index 9446e5bb2..bff6e2f13 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingTextMsgBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingTextMsgBubbleCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.userNameLabel.textColor = kRiotPrimaryTextColor; - self.messageTextView.tintColor = kRiotColorGreen; + self.userNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m index ff4957f6d..85d4afb55 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingTextMsgWithPaginationTitleBubbleCell @@ -25,8 +26,8 @@ { [super customizeTableViewCellRendering]; - self.paginationLabel.textColor = kRiotColorGreen; - self.paginationSeparatorView.backgroundColor = kRiotColorGreen; + self.paginationLabel.textColor = ThemeService.shared.theme.tintColor; + self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXKCellData *)cellData diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m index 5274fb96b..de0205036 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m @@ -17,7 +17,7 @@ #import "RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" @implementation RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m index 1a3b829b1..1e0ffeb61 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomOutgoingTextMsgWithoutSenderInfoBubbleCell @@ -25,7 +26,7 @@ { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotColorGreen; + self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m index b9f1b08d0..b5b077c4e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m @@ -17,7 +17,7 @@ #import "RoomOutgoingTextMsgWithoutSenderNameBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" @implementation RoomOutgoingTextMsgWithoutSenderNameBubbleCell diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomPredecessorBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomPredecessorBubbleCell.m index 154709567..9099d6815 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomPredecessorBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomPredecessorBubbleCell.m @@ -17,7 +17,8 @@ #import "RoomPredecessorBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #pragma mark - Defines & Constants @@ -41,6 +42,8 @@ static CGFloat const kCustomBackgroundCornerRadius = 5.0; { [super awakeFromNib]; + // Disable text selection and link interaction + self.messageTextView.selectable = NO; self.customBackgroundView.layer.masksToBounds = YES; } @@ -57,8 +60,8 @@ static CGFloat const kCustomBackgroundCornerRadius = 5.0; { [super customizeTableViewCellRendering]; - self.messageTextView.tintColor = kRiotPrimaryTextColor; - self.customBackgroundView.backgroundColor = kRiotSecondaryBgColor; + self.messageTextView.tintColor = ThemeService.shared.theme.textPrimaryColor; + self.customBackgroundView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; } @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomSelectedStickerBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomSelectedStickerBubbleCell.m index 18a757bbb..f63b5180c 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomSelectedStickerBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomSelectedStickerBubbleCell.m @@ -24,7 +24,8 @@ #import "RoomIncomingEncryptedAttachmentBubbleCell.h" #import "RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomSelectedStickerBubbleCell @@ -44,8 +45,8 @@ arrowMaskLayer.path = path.CGPath; self.arrowView.layer.mask = arrowMaskLayer; - self.arrowView.backgroundColor = kRiotSecondaryBgColor; - self.descriptionView.backgroundColor = kRiotSecondaryBgColor; + self.arrowView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.descriptionView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; [self.descriptionView.layer setCornerRadius:10]; } diff --git a/Riot/Modules/Room/Views/Event/EventDetailsView.m b/Riot/Modules/Room/Views/Event/EventDetailsView.m index 17f7b2178..3f30e3987 100644 --- a/Riot/Modules/Room/Views/Event/EventDetailsView.m +++ b/Riot/Modules/Room/Views/Event/EventDetailsView.m @@ -16,7 +16,8 @@ #import "EventDetailsView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation EventDetailsView @@ -24,11 +25,11 @@ { [super customizeViewRendering]; - self.backgroundColor = kRiotSecondaryBgColor; - self.textView.backgroundColor = kRiotPrimaryBgColor; - self.textView.textColor = kRiotPrimaryTextColor; - self.redactButton.tintColor = kRiotColorGreen; - self.closeButton.tintColor = kRiotColorGreen; + self.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.textView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.textView.textColor = ThemeService.shared.theme.textPrimaryColor; + self.redactButton.tintColor = ThemeService.shared.theme.tintColor; + self.closeButton.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m index 0ed84f45e..d3dce27e5 100644 --- a/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m @@ -16,7 +16,8 @@ #import "DisabledRoomInputToolbarView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation DisabledRoomInputToolbarView @@ -47,11 +48,11 @@ // Remove default toolbar background color self.backgroundColor = [UIColor clearColor]; - self.separatorView.backgroundColor = kRiotAuxiliaryColor; + self.separatorView.backgroundColor = ThemeService.shared.theme.headerTextSecondaryColor; self.disabledReasonTextView.font = [UIFont systemFontOfSize:15]; - self.disabledReasonTextView.textColor = kRiotPrimaryTextColor; - self.disabledReasonTextView.tintColor = kRiotColorGreen; + self.disabledReasonTextView.textColor = ThemeService.shared.theme.textPrimaryColor; + self.disabledReasonTextView.tintColor = ThemeService.shared.theme.tintColor; self.disabledReasonTextView.editable = NO; self.disabledReasonTextView.scrollEnabled = NO; } diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index feae0f022..577592a41 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -17,7 +17,8 @@ #import "RoomInputToolbarView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "GBDeviceInfo_iOS.h" @@ -71,8 +72,8 @@ self.rightInputToolbarButton.hidden = YES; - [self.rightInputToolbarButton setTitleColor:kRiotColorGreen forState:UIControlStateNormal]; - [self.rightInputToolbarButton setTitleColor:kRiotColorGreen forState:UIControlStateHighlighted]; + [self.rightInputToolbarButton setTitleColor:ThemeService.shared.theme.tintColor forState:UIControlStateNormal]; + [self.rightInputToolbarButton setTitleColor:ThemeService.shared.theme.tintColor forState:UIControlStateHighlighted]; self.isEncryptionEnabled = _isEncryptionEnabled; } @@ -86,7 +87,7 @@ // Remove default toolbar background color self.backgroundColor = [UIColor clearColor]; - self.separatorView.backgroundColor = kRiotAuxiliaryColor; + self.separatorView.backgroundColor = ThemeService.shared.theme.headerTextSecondaryColor; // Custom the growingTextView display growingTextView.layer.cornerRadius = 0; @@ -94,10 +95,10 @@ growingTextView.backgroundColor = [UIColor clearColor]; growingTextView.font = [UIFont systemFontOfSize:15]; - growingTextView.textColor = kRiotPrimaryTextColor; - growingTextView.tintColor = kRiotColorGreen; + growingTextView.textColor = ThemeService.shared.theme.textPrimaryColor; + growingTextView.tintColor = ThemeService.shared.theme.tintColor; - growingTextView.internalTextView.keyboardAppearance = kRiotKeyboard; + growingTextView.internalTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance; } #pragma mark - diff --git a/Riot/Modules/Room/Views/Title/Avatar/RoomAvatarTitleView.m b/Riot/Modules/Room/Views/Title/Avatar/RoomAvatarTitleView.m index 7df739f40..9e7bc5a07 100644 --- a/Riot/Modules/Room/Views/Title/Avatar/RoomAvatarTitleView.m +++ b/Riot/Modules/Room/Views/Title/Avatar/RoomAvatarTitleView.m @@ -17,7 +17,7 @@ #import "RoomAvatarTitleView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "MXRoomSummary+Riot.h" diff --git a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.h b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.h index 47073ca77..ff6a1b40d 100644 --- a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.h +++ b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.h @@ -27,4 +27,7 @@ @property (weak, nonatomic) IBOutlet UIView *bottomBorderView; -@end \ No newline at end of file +@property (weak, nonatomic) IBOutlet UIImageView *membersListIcon; +@property (weak, nonatomic) IBOutlet UIImageView *addParticipantIcon; + +@end diff --git a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.m b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.m index f8ef4bc72..cd2972cbb 100644 --- a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.m @@ -17,7 +17,8 @@ #import "ExpandedRoomTitleView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXRoomSummary+Riot.h" @@ -34,13 +35,24 @@ [super awakeFromNib]; } +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.membersListIcon.image = [MXKTools paintImage:self.membersListIcon.image + withColor:ThemeService.shared.theme.tintColor]; + + // TODO: paintImage does not work here because addParticipantIcon has 2 colors +// self.addParticipantIcon.image = [MXKTools paintImage:self.addParticipantIcon.image +// withColor:ThemeService.shared.theme.accent]; +} + -(void)customizeViewRendering { [super customizeViewRendering]; - self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); - self.roomTopic.textColor = kRiotTopicTextColor; - self.roomMembers.textColor = kRiotColorGreen; + self.roomTopic.textColor = ThemeService.shared.theme.baseTextSecondaryColor; + self.roomMembers.textColor = ThemeService.shared.theme.tintColor; } - (void)refreshDisplay @@ -51,17 +63,6 @@ { [self.mxRoom.summary setRoomAvatarImageIn:self.roomAvatar]; - self.displayNameTextField.text = self.mxRoom.summary.displayname; - if (!self.displayNameTextField.text.length) - { - self.displayNameTextField.text = [NSBundle mxk_localizedStringForKey:@"room_displayname_empty_room"]; - self.displayNameTextField.textColor = kRiotSecondaryTextColor; - } - else - { - self.displayNameTextField.textColor = kRiotPrimaryTextColor; - } - self.roomTopic.text = [MXTools stripNewlineCharacters:self.mxRoom.summary.topic]; // Compute active members count @@ -130,7 +131,7 @@ self.roomAvatar.layer.cornerRadius = self.roomAvatar.frame.size.width / 2; self.roomAvatar.clipsToBounds = YES; - self.roomAvatar.defaultBackgroundColor = kRiotSecondaryBgColor; + self.roomAvatar.defaultBackgroundColor = ThemeService.shared.theme.headerBackgroundColor; // Force the layout of subviews to update the position of 'bottomBorderView' which is used to define the actual height of the preview container. [self layoutIfNeeded]; diff --git a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.xib b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.xib index 2c15d26ca..353189cdd 100644 --- a/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.xib +++ b/Riot/Modules/Room/Views/Title/Expanded/ExpandedRoomTitleView.xib @@ -1,12 +1,11 @@ - + - - + @@ -38,7 +37,7 @@ - + @@ -62,7 +61,7 @@ - + @@ -143,9 +142,11 @@ + + @@ -157,7 +158,7 @@ - + diff --git a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m index d1c155736..fc4f74a7b 100644 --- a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m @@ -18,7 +18,8 @@ #import "PreviewRoomTitleView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "MXRoomSummary+Riot.h" @@ -61,31 +62,30 @@ -(void)customizeViewRendering { [super customizeViewRendering]; + + // Use same color as navigation bar + self.mainHeaderBackground.backgroundColor = ThemeService.shared.theme.baseColor; + - self.backgroundColor = kRiotPrimaryBgColor; - self.mainHeaderBackground.backgroundColor = kRiotSecondaryBgColor; + self.roomTopic.textColor = ThemeService.shared.theme.baseTextSecondaryColor; - self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + self.roomMembers.textColor = ThemeService.shared.theme.tintColor; - self.roomTopic.textColor = kRiotTopicTextColor; - - self.roomMembers.textColor = kRiotColorGreen; - - self.previewLabel.textColor = kRiotTopicTextColor; + self.previewLabel.textColor = ThemeService.shared.theme.baseTextSecondaryColor; self.previewLabel.numberOfLines = 0; - self.subNoticeLabel.textColor = kRiotSecondaryTextColor; + self.subNoticeLabel.textColor = ThemeService.shared.theme.textSecondaryColor; self.subNoticeLabel.numberOfLines = 0; - self.bottomBorderView.backgroundColor = kRiotSecondaryBgColor; + self.bottomBorderView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; [self.leftButton.layer setCornerRadius:5]; self.leftButton.clipsToBounds = YES; - self.leftButton.backgroundColor = kRiotColorGreen; + self.leftButton.backgroundColor = ThemeService.shared.theme.tintColor; [self.rightButton.layer setCornerRadius:5]; self.rightButton.clipsToBounds = YES; - self.rightButton.backgroundColor = kRiotColorGreen; + self.rightButton.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)refreshDisplay @@ -102,7 +102,8 @@ andImageOrientation:UIImageOrientationUp toFitViewSize:self.roomAvatar.frame.size withMethod:MXThumbnailingMethodCrop - previewImage:[UIImage imageNamed:@"placeholder"] + previewImage:[MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor] mediaManager:self.mxRoom.mxSession.mediaManager]; } else @@ -173,18 +174,6 @@ { [self.mxRoom.summary setRoomAvatarImageIn:self.roomAvatar]; - // The user is here invited to join a room (This invitation has been received from server sync) - self.displayNameTextField.text = self.mxRoom.summary.displayname; - if (!self.displayNameTextField.text.length) - { - self.displayNameTextField.text = [NSBundle mxk_localizedStringForKey:@"room_displayname_empty_room"]; - self.displayNameTextField.textColor = kRiotSecondaryTextColor; - } - else - { - self.displayNameTextField.textColor = kRiotPrimaryTextColor; - } - // Display room topic self.roomTopic.text = [MXTools stripNewlineCharacters:self.mxRoom.summary.topic]; @@ -260,7 +249,7 @@ self.roomAvatar.layer.cornerRadius = self.roomAvatar.frame.size.width / 2; self.roomAvatar.clipsToBounds = YES; - self.roomAvatar.defaultBackgroundColor = kRiotSecondaryBgColor; + self.roomAvatar.defaultBackgroundColor = ThemeService.shared.theme.headerBackgroundColor; // Force the layout of subviews to update the position of 'bottomBorderView' which is used to define the actual height of the preview container. [self layoutIfNeeded]; diff --git a/Riot/Modules/Room/Views/Title/RoomTitleView.m b/Riot/Modules/Room/Views/Title/RoomTitleView.m index ba8bfc421..7b38994ae 100644 --- a/Riot/Modules/Room/Views/Title/RoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/RoomTitleView.m @@ -17,7 +17,8 @@ #import "RoomTitleView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation RoomTitleView @@ -70,6 +71,9 @@ - (void)layoutSubviews { [super layoutSubviews]; + + self.roomDetailsIconImageView.image = [MXKTools paintImage:self.roomDetailsIconImageView.image + withColor:ThemeService.shared.theme.tintColor]; if (self.superview) { @@ -132,8 +136,10 @@ -(void)customizeViewRendering { [super customizeViewRendering]; - - self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + + // Use same color as navigation bar + self.backgroundColor = ThemeService.shared.theme.baseColor; + self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.baseTextPrimaryColor : ThemeService.shared.theme.textSecondaryColor); } - (void)setRoomPreviewData:(RoomPreviewData *)roomPreviewData @@ -158,11 +164,11 @@ if (!self.displayNameTextField.text.length) { self.displayNameTextField.text = [NSBundle mxk_localizedStringForKey:@"room_displayname_empty_room"]; - self.displayNameTextField.textColor = kRiotSecondaryTextColor; + self.displayNameTextField.textColor = ThemeService.shared.theme.textSecondaryColor; } else { - self.displayNameTextField.textColor = kRiotPrimaryTextColor; + self.displayNameTextField.textColor = ThemeService.shared.theme.baseTextPrimaryColor; } } } diff --git a/Riot/Modules/Room/Views/Title/Simple/SimpleRoomTitleView.m b/Riot/Modules/Room/Views/Title/Simple/SimpleRoomTitleView.m index bffc71c54..db5fcc5e7 100644 --- a/Riot/Modules/Room/Views/Title/Simple/SimpleRoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/Simple/SimpleRoomTitleView.m @@ -17,7 +17,7 @@ #import "SimpleRoomTitleView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" @implementation SimpleRoomTitleView @@ -87,30 +87,5 @@ } } --(void)customizeViewRendering -{ - [super customizeViewRendering]; - - self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); -} - -- (void)refreshDisplay -{ - [super refreshDisplay]; - - if (self.mxRoom) - { - self.displayNameTextField.text = self.mxRoom.summary.displayname; - if (!self.displayNameTextField.text.length) - { - self.displayNameTextField.text = [NSBundle mxk_localizedStringForKey:@"room_displayname_empty_room"]; - self.displayNameTextField.textColor = kRiotSecondaryTextColor; - } - else - { - self.displayNameTextField.textColor = kRiotPrimaryTextColor; - } - } -} @end diff --git a/Riot/Modules/Rooms/DirectoryPicker/DirectoryServerPickerViewController.m b/Riot/Modules/Rooms/DirectoryPicker/DirectoryServerPickerViewController.m index a7450b622..a72c3d372 100644 --- a/Riot/Modules/Rooms/DirectoryPicker/DirectoryServerPickerViewController.m +++ b/Riot/Modules/Rooms/DirectoryPicker/DirectoryServerPickerViewController.m @@ -19,6 +19,7 @@ #import "DirectoryServerDetailTableViewCell.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface DirectoryServerPickerViewController () { @@ -35,8 +36,8 @@ // Current request in progress. MXHTTPOperation *mxCurrentOperation; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -57,10 +58,10 @@ dataSource = nil; onCompleteBlock = nil; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (kAppDelegateDidTapStatusBarNotificationObserver) @@ -109,7 +110,7 @@ self.tableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -119,12 +120,12 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -135,7 +136,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -225,13 +226,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -311,11 +312,11 @@ { typeof(self) self = weakSelf; - UITextField *textField = [self->currentAlert textFields].firstObject; + NSString *text = [self->currentAlert textFields].firstObject.text; self->currentAlert = nil; - NSString *homeserver = textField.text; + NSString *homeserver = text; if (homeserver.length) { // Test if the homeserver exists diff --git a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerDetailTableViewCell.m b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerDetailTableViewCell.m index da8649181..dfa59ad8e 100644 --- a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerDetailTableViewCell.m +++ b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerDetailTableViewCell.m @@ -16,7 +16,8 @@ #import "DirectoryServerDetailTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation DirectoryServerDetailTableViewCell @@ -24,7 +25,7 @@ { [super customizeTableViewCellRendering]; - self.detailDescLabel.textColor = kRiotSecondaryTextColor; + self.detailDescLabel.textColor = ThemeService.shared.theme.textSecondaryColor; } - (void)render:(id)cellData diff --git a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m index 617b0b400..3c1ceeabe 100644 --- a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m +++ b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m @@ -18,7 +18,8 @@ #import "DirectoryServerTableViewCell.h" #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation DirectoryServerTableViewCell @@ -28,7 +29,7 @@ { [super customizeTableViewCellRendering]; - self.descLabel.textColor = kRiotPrimaryTextColor; + self.descLabel.textColor = ThemeService.shared.theme.textPrimaryColor; } - (void)layoutSubviews @@ -62,7 +63,8 @@ [self.iconImageView setImageURI:iconURL withType:nil andImageOrientation:UIImageOrientationUp - previewImage:[UIImage imageNamed:@"placeholder"] + previewImage:[MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + withColor:ThemeService.shared.theme.tintColor] mediaManager:cellData.mediaManager]; } else diff --git a/Riot/Modules/Rooms/RoomsViewController.m b/Riot/Modules/Rooms/RoomsViewController.m index 64b6c121f..47731c963 100644 --- a/Riot/Modules/Rooms/RoomsViewController.m +++ b/Riot/Modules/Rooms/RoomsViewController.m @@ -22,6 +22,8 @@ #import "DirectoryServerPickerViewController.h" +#import "Riot-Swift.h" + @interface RoomsViewController () { RecentsDataSource *recentsDataSource; @@ -62,6 +64,7 @@ [super viewWillAppear:animated]; [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_rooms", @"Vector", nil); + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor; if ([self.dataSource isKindOfClass:RecentsDataSource.class]) { diff --git a/Riot/Modules/Settings/DeactivateAccount/DeactivateAccountViewController.m b/Riot/Modules/Settings/DeactivateAccount/DeactivateAccountViewController.m index dfbd4f8ca..975146130 100644 --- a/Riot/Modules/Settings/DeactivateAccount/DeactivateAccountViewController.m +++ b/Riot/Modules/Settings/DeactivateAccount/DeactivateAccountViewController.m @@ -16,7 +16,8 @@ #import "DeactivateAccountViewController.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "AppDelegate.h" #pragma mark - Defines & Constants @@ -108,36 +109,36 @@ static CGFloat const kTextFontSize = 15.0; - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } #pragma mark - Private - (void)registerThemeNotification { - self.themeDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + self.themeDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; }]; } - (void)userInterfaceThemeDidChange { - self.view.backgroundColor = kRiotPrimaryBgColor; - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; } - (void)setupStringAttributes { self.normalStringAttributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:kTextFontSize], - NSForegroundColorAttributeName: kRiotPrimaryTextColor + NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor }; self.emphasizeStringAttributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:kTextFontSize weight:UIFontWeightBold], - NSForegroundColorAttributeName: kRiotPrimaryTextColor + NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor }; } @@ -151,7 +152,7 @@ static CGFloat const kTextFontSize = 15.0; - (void)setupNavigationBar { - self.navigationController.navigationBar.titleTextAttributes = @{ NSForegroundColorAttributeName: kRiotColorRed }; + self.navigationController.navigationBar.titleTextAttributes = @{ NSForegroundColorAttributeName: ThemeService.shared.theme.warningColor }; UIBarButtonItem *cancelBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"cancel", @"Vector", nil) style:UIBarButtonItemStylePlain target:self action:@selector(cancelButtonAction:)]; self.navigationItem.rightBarButtonItem = cancelBarButtonItem; @@ -165,9 +166,9 @@ static CGFloat const kTextFontSize = 15.0; self.deactivateAcccountButton.titleLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; self.deactivateAcccountButton.layer.masksToBounds = YES; - self.deactivateAcccountButton.backgroundColor = kRiotColorGreen; + self.deactivateAcccountButton.backgroundColor = ThemeService.shared.theme.tintColor; [self.deactivateAcccountButton setTitle:NSLocalizedStringFromTable(@"deactivate_account_validate_action", @"Vector", nil) forState:UIControlStateNormal]; - [self.deactivateAcccountButton setTitleColor:kRiotColorSilver forState:UIControlStateDisabled]; + [self.deactivateAcccountButton setTitleColor:ThemeService.shared.theme.headerTextSecondaryColor forState:UIControlStateDisabled]; } - (void)setupDeactivateAccountInfosLabel diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift new file mode 100644 index 000000000..549928268 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -0,0 +1,494 @@ +/* + 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 + +@objc protocol SettingsKeyBackupTableViewSectionDelegate: class { + func settingsKeyBackupTableViewSectionDidUpdate(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) + + func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView + func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton + + + func settingsKeyBackupTableViewSectionShowKeyBackupSetup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion) + + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showActivityIndicator show:Bool) + func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error:Error) +} + +@objc final class SettingsKeyBackupTableViewSection: NSObject { + + // MARK: - Properties + + @objc weak var delegate: SettingsKeyBackupTableViewSectionDelegate? + + // MARK: Private + + // This view class holds the model because the model is in pure Swift + // whereas this class can be used from objC + private var viewModel: SettingsKeyBackupViewModelType! + + // Need to know the state to make `cellForRow` deliver cells accordingly + private var viewState: SettingsKeyBackupViewState = .checkingBackup + + private var userDevice: MXDeviceInfo + + // MARK: - Public + + @objc init(withKeyBackup keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) { + self.viewModel = SettingsKeyBackupViewModel(keyBackup: keyBackup) + self.userDevice = userDevice + super.init() + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .load) + } + + @objc func numberOfRows() -> Int { + var numberOfRows: Int + + switch self.viewState { + case .checkingBackup: + numberOfRows = self.numberOfCheckingBackupRows() + case .noBackup: + numberOfRows = self.numberOfNoBackupRows() + case .backup: + numberOfRows = self.numberOfBackupRows() + case .backupAndRunning: + numberOfRows = self.numberOfBackupAndRunningRows() + case .backupNotTrusted: + numberOfRows = self.numberOfBackupNotTrustedRows() + } + + return numberOfRows + } + + @objc func cellForRow(atRow row:Int) -> UITableViewCell { + var cell: UITableViewCell + + switch self.viewState { + case .checkingBackup: + cell = self.renderCheckingBackupCell(atRow:row) + + case .noBackup: + cell = self.renderNoBackupCell(atRow:row) + + case .backup(let keyBackupVersion, let keyBackupVersionTrust): + cell = self.renderBackupCell(atRow: row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust) + + case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): + cell = self.renderBackupAndRunningCell(atRow:row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust, + backupProgress: backupProgress) + + case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + cell = self.renderBackupNotTrustedCell(atRow:row, + keyBackupVersion: keyBackupVersion, + keyBackupVersionTrust: keyBackupVersionTrust) + } + + return cell + } + + @objc func reload() { + self.viewModel.process(viewAction: .load) + } + + @objc func delete(keyBackupVersion: MXKeyBackupVersion) { + self.viewModel.process(viewAction: .delete(keyBackupVersion)) + } + + + // MARK: - Pseudo TableView datasource + + private func numberOfCheckingBackupRows() -> Int { + return 1 + } + + private func renderCheckingBackupCell(atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let info = VectorL10n.settingsKeyBackupInfo + let checking = VectorL10n.settingsKeyBackupInfoChecking + + let strings = [info, "", checking] + cell.mxkTextView.text = strings.joined(separator: "\n") + + return cell + } + + + private func numberOfNoBackupRows() -> Int { + return 2 + } + + private func renderNoBackupCell(atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let noBackup = VectorL10n.settingsKeyBackupInfoNone + let info = VectorL10n.settingsKeyBackupInfo + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + + let strings = [noBackup, "", info, "", signoutWarning] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + cell = self.buttonCellForCreate(atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + private func numberOfBackupRows() -> Int { + return 5 + } + + private func renderBackupCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let info = VectorL10n.settingsKeyBackupInfo + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + + let strings = [info, "", backupStatus] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone + + let strings = [version, algorithm, uploadStatus] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 2: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 3: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 4: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + private func numberOfBackupAndRunningRows() -> Int { + return 5 + } + + private func renderBackupAndRunningCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust, backupProgress: Progress) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0) + + let info = VectorL10n.settingsKeyBackupInfo + let backupStatus = VectorL10n.settingsKeyBackupInfoValid + + let strings = [info, "", backupStatus] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining)) + + let strings = [version, algorithm, uploadStatus] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 2: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 3: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row) + + case 4: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + private func numberOfBackupNotTrustedRows() -> Int { + return 5 + } + + private func renderBackupNotTrustedCell(atRow row: Int, keyBackupVersion: MXKeyBackupVersion, keyBackupVersionTrust: MXKeyBackupVersionTrust) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + var cell: UITableViewCell + switch row { + case 0: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let info = VectorL10n.settingsKeyBackupInfo + let backupStatus = VectorL10n.settingsKeyBackupInfoNotValid + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + + let strings = [info, "", backupStatus, "", signoutWarning] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 1: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "") + let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm) + + let strings = [version, algorithm] + infoCell.mxkTextView.text = strings.joined(separator: "\n") + + cell = infoCell + + case 2: + let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row) + + let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust); + infoCell.mxkTextView.text = backupTrust.joined(separator: "\n") + + cell = infoCell + + case 3: + cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, atRow: row, title: VectorL10n.settingsKeyBackupButtonUse) + + case 4: + cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row) + + default: + cell = UITableViewCell.init() + } + + return cell + } + + + // MARK: - Data Computing + + private func stringForKeyBackupTrust(_ keyBackupVersionTrust: MXKeyBackupVersionTrust) -> [String] { + + return keyBackupVersionTrust.signatures.map { (signature) -> String in + guard let device = signature.device else { + return VectorL10n.settingsKeyBackupInfoTrustSignatureUnknown(signature.deviceId) + } + + let displayName = device.displayName ?? device.deviceId ?? "" + + if device.fingerprint == self.userDevice.fingerprint { + return VectorL10n.settingsKeyBackupInfoTrustSignatureValid + } + else if signature.valid && (device.verified == MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceVerified(displayName) + } + else if signature.valid && (device.verified != MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(displayName) + } + else if !signature.valid && (device.verified == MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(displayName) + } + else if !signature.valid && (device.verified != MXDeviceVerified) { + return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(displayName) + } + + return ""; + } + } + + private func lastNonVerifiedDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> MXDeviceInfo? + { + var lastNonVerifiedDevice: MXDeviceInfo? + for signature in keyBackupVersionTrust.signatures.reversed() { + + guard let device = signature.device else { + continue + } + + if device.verified != MXDeviceVerified + { + lastNonVerifiedDevice = device + break + } + } + return lastNonVerifiedDevice + } + + private func lastUnVerifiableDevice(_ keyBackupVersionTrust:MXKeyBackupVersionTrust) -> String? + { + var lastUnVerifiableDevice: String? + for signature in keyBackupVersionTrust.signatures.reversed() { + + if signature.device == nil { + lastUnVerifiableDevice = signature.deviceId + break + } + } + return lastUnVerifiableDevice + } + + // MARK: - Button cells + + private func buttonCellForCreate(atRow row: Int) -> UITableViewCell { + + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) + + let btnTitle = VectorL10n.settingsKeyBackupButtonCreate + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .create) + } + + return cell + } + + private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, atRow row: Int, title: String = VectorL10n.settingsKeyBackupButtonRestore) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) + cell.mxkButton.setTitle(title, for: .normal) + cell.mxkButton.setTitle(title, for: .highlighted) + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .restore(keyBackupVersion)) + } + return cell + } + + private func buttonCellForDelete(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell.init() + } + + let cell:MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row) + let btnTitle = VectorL10n.settingsKeyBackupButtonDelete + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .confirmDelete(keyBackupVersion)) + } + + return cell + } +} + + +// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate +extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDelegate { + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) { + self.viewState = viewState + + // The tableview datasource will call `self.cellForRow()` + self.delegate?.settingsKeyBackupTableViewSectionDidUpdate(self) + } + + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) { + switch networkRequestViewSate { + case .loading: + self.delegate?.settingsKeyBackup(self, showActivityIndicator: true) + case .loaded: + self.delegate?.settingsKeyBackup(self, showActivityIndicator: false) + case .error(let error): + self.delegate?.settingsKeyBackup(self, showError: error) + } + } + + func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) { + self.delegate?.settingsKeyBackupTableViewSectionShowKeyBackupSetup(self) + } + + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) + } + + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion) + } +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift new file mode 100644 index 000000000..7d6e05166 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewAction.swift @@ -0,0 +1,25 @@ +/* + 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 + +enum SettingsKeyBackupViewAction { + case load + case create + case restore(MXKeyBackupVersion) + case confirmDelete(MXKeyBackupVersion) + case delete(MXKeyBackupVersion) +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift new file mode 100644 index 000000000..c8a265684 --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModel.swift @@ -0,0 +1,153 @@ +/* + 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 SettingsKeyBackupViewModel: SettingsKeyBackupViewModelType { + + // MARK: - Properties + weak var viewDelegate: SettingsKeyBackupViewModelViewDelegate? + + // MARK: Private + private let keyBackup: MXKeyBackup + + init(keyBackup: MXKeyBackup) { + self.keyBackup = keyBackup + self.registerKeyBackupVersionDidChangeStateNotification() + } + + private func registerKeyBackupVersionDidChangeStateNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(keyBackupDidStateChange), name: NSNotification.Name.mxKeyBackupDidStateChange, object: self.keyBackup) + } + + @objc private func keyBackupDidStateChange() { + self.checkKeyBackupState() + } + + func process(viewAction: SettingsKeyBackupViewAction) { + guard let viewDelegate = self.viewDelegate else { + return + } + + switch viewAction { + case .load: + viewDelegate.settingsKeyBackupViewModel(self, didUpdateViewState: .checkingBackup) + self.checkKeyBackupState() + case .create: + viewDelegate.settingsKeyBackupViewModelShowKeyBackupSetup(self) + break + case .restore(let keyBackupVersion): + viewDelegate.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion) + break + case .confirmDelete(let keyBackupVersion): + viewDelegate.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion) + break + case .delete(let keyBackupVersion): + self.deleteKeyBackupVersion(keyBackupVersion) + break + } + } + + // MARK: - Private + + private func checkKeyBackupState() { + + // Check homeserver update in background + self.keyBackup.forceRefresh(nil, failure: nil) + + if let keyBackupVersion = self.keyBackup.keyBackupVersion { + + self.keyBackup.trust(for: keyBackupVersion, onComplete: { [weak self] (keyBackupVersionTrust) in + + guard let sself = self else { + return + } + + sself.computeState(withBackupVersionTrust:keyBackupVersionTrust) + }) + } + else { + computeState() + } + } + + private func computeState(withBackupVersionTrust keyBackupVersionTrust:MXKeyBackupVersionTrust? = nil) { + + var viewState: SettingsKeyBackupViewState? + switch self.keyBackup.state { + + case MXKeyBackupStateUnknown, + MXKeyBackupStateCheckingBackUpOnHomeserver: + viewState = .checkingBackup + + case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling: + viewState = .noBackup + + case MXKeyBackupStateNotTrusted: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backupNotTrusted(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateReadyToBackUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backup(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + + // Get the backup progress before updating the state + self.keyBackup.backupProgress { [weak self] (progress) in + guard let sself = self else { + return + } + + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateViewState: .backupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) + } + default: + break + } + + if let vviewState = viewState { + self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateViewState: vviewState) + } + } + + private func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) { + guard let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + self.viewDelegate?.settingsKeyBackupViewModel(self, didUpdateNetworkRequestViewState: .loading) + + self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { [weak self] () in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateNetworkRequestViewState: .loaded) + + }, failure: { [weak self] error in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsKeyBackupViewModel(sself, didUpdateNetworkRequestViewState: .error(error)) + }) + } +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift new file mode 100644 index 000000000..f7cc5e3bf --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -0,0 +1,33 @@ +/* + 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 + +protocol SettingsKeyBackupViewModelViewDelegate: class { + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) + func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) + + func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion:MXKeyBackupVersion) + func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion:MXKeyBackupVersion) +} + +protocol SettingsKeyBackupViewModelType { + + var viewDelegate: SettingsKeyBackupViewModelViewDelegate? { get set } + + func process(viewAction: SettingsKeyBackupViewAction) +} diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift new file mode 100644 index 000000000..9751e402e --- /dev/null +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewState.swift @@ -0,0 +1,41 @@ +/* + 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 + +/// SettingsKeyBackup view state +/// +/// - checkingBackup: Load current backup on the homeserver +/// - checkError: Fail to load current backup data +/// - noBackup: There is no backup on the homeserver +/// - backup: There is a valid backup on the homeserver. All keys have been backed up to it +/// - backupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it +/// - backupButNotVerified: There is a backup on the homeserver but it has not been verified yet +enum SettingsKeyBackupViewState { + case checkingBackup + case noBackup + case backup(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case backupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) +} + +/// State representing a network request made by the module +/// Only SettingsKeyBackupViewAction.delete generates such states +enum SettingsKeyBackupNetworkRequestViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/Settings/Language/LanguagePickerViewController.m b/Riot/Modules/Settings/Language/LanguagePickerViewController.m index ef3ac56d8..915de9864 100644 --- a/Riot/Modules/Settings/Language/LanguagePickerViewController.m +++ b/Riot/Modules/Settings/Language/LanguagePickerViewController.m @@ -17,13 +17,14 @@ #import "LanguagePickerViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface LanguagePickerViewController () { /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; /** The fake top view displayed in case of vertical bounce. @@ -58,7 +59,7 @@ [self.tableView addSubview:topview]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -68,16 +69,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.searchBar.barStyle = kRiotDesignSearchBarStyle; - self.searchBar.tintColor = kRiotDesignSearchBarTintColor; + [ThemeService.shared.theme applyStyleOnSearchBar:self.searchBar]; // Use the primary bg color for the table view in plain style. - self.tableView.backgroundColor = kRiotPrimaryBgColor; - topview.backgroundColor = kRiotPrimaryBgColor; + self.tableView.backgroundColor = ThemeService.shared.theme.backgroundColor; + topview.backgroundColor = ThemeService.shared.theme.backgroundColor; if (self.tableView.dataSource) { @@ -87,7 +87,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy @@ -97,10 +97,10 @@ [topview removeFromSuperview]; topview = nil; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -114,15 +114,15 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.textLabel.textColor = kRiotPrimaryTextColor; - cell.detailTextLabel.textColor = kRiotSecondaryTextColor; - cell.backgroundColor = kRiotPrimaryBgColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + cell.detailTextLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Settings/PhoneCountry/CountryPickerViewController.m b/Riot/Modules/Settings/PhoneCountry/CountryPickerViewController.m index 1c0a2fa40..ef7943c5f 100644 --- a/Riot/Modules/Settings/PhoneCountry/CountryPickerViewController.m +++ b/Riot/Modules/Settings/PhoneCountry/CountryPickerViewController.m @@ -17,13 +17,14 @@ #import "CountryPickerViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface CountryPickerViewController () { /** - Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. + Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kRiotDesignValuesDidChangeThemeNotificationObserver; + id kThemeServiceDidChangeThemeNotificationObserver; /** The fake top view displayed in case of vertical bounce. @@ -61,7 +62,7 @@ [self.tableView addSubview:topview]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -71,16 +72,15 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; - self.searchBar.barStyle = kRiotDesignSearchBarStyle; - self.searchBar.tintColor = kRiotDesignSearchBarTintColor; + [ThemeService.shared.theme applyStyleOnSearchBar:self.searchBar]; // Use the primary bg color for the table view in plain style. - self.tableView.backgroundColor = kRiotPrimaryBgColor; - topview.backgroundColor = kRiotPrimaryBgColor; + self.tableView.backgroundColor = ThemeService.shared.theme.backgroundColor; + topview.backgroundColor = ThemeService.shared.theme.backgroundColor; self.searchDisplayController.searchResultsTableView.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -91,7 +91,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -109,24 +109,24 @@ [topview removeFromSuperview]; topview = nil; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.textLabel.textColor = kRiotPrimaryTextColor; - cell.detailTextLabel.textColor = kRiotSecondaryTextColor; - cell.backgroundColor = kRiotPrimaryBgColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + cell.detailTextLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index cd55bd79f..7df4871e3 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -38,7 +38,7 @@ #import "NBPhoneNumberUtil.h" #import "RageShakeManager.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "TableViewCellWithPhoneNumberTextField.h" #import "GroupsDataSource.h" @@ -48,6 +48,8 @@ #import "Riot-Swift.h" +#import "EncryptionInfoView.h" + NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId"; enum @@ -63,8 +65,9 @@ enum SETTINGS_SECTION_OTHER_INDEX, SETTINGS_SECTION_LABS_INDEX, SETTINGS_SECTION_CRYPTOGRAPHY_INDEX, - SETTINGS_SECTION_FLAIR_INDEX, + SETTINGS_SECTION_KEYBACKUP_INDEX, SETTINGS_SECTION_DEVICES_INDEX, + SETTINGS_SECTION_FLAIR_INDEX, SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX, SETTINGS_SECTION_COUNT }; @@ -135,7 +138,12 @@ enum { typedef void (^blockSettingsViewController_onReadyToDestroy)(void); -@interface SettingsViewController () +@interface SettingsViewController () { // Current alert (if any). UIAlertController *currentAlert; @@ -202,8 +210,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar. id kAppDelegateDidTapStatusBarNotificationObserver; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; // Postpone destroy operation when saving, pwd reset or email binding is in progress BOOL isSavingInProgress; @@ -227,6 +235,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // The current pushed view controller UIViewController *pushedViewController; + + SettingsKeyBackupTableViewSection *keyBackupSection; + KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; + KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter; } /** @@ -239,7 +251,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); */ @property (nonatomic) BOOL newPhoneEditingEnabled; -@property (weak, nonatomic) DeactivateAccountViewController *deactivateAccountViewController; +@property (nonatomic, weak) DeactivateAccountViewController *deactivateAccountViewController; +@property (nonatomic, strong) SignOutAlertPresenter *signOutAlertPresenter; +@property (nonatomic, weak) UIButton *signOutButton; @end @@ -314,7 +328,19 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { [self addMatrixSession:mxSession]; } - + + if (self.mainSession.crypto.backup) + { + MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId + deviceId:self.mainSession.matrixRestClient.credentials.deviceId]; + + if (deviceInfo) + { + keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; + keyBackupSection.delegate = self; + } + } + groupsDataSource = [[GroupsDataSource alloc] initWithMatrixSession:self.mainSession]; [groupsDataSource finalizeInitialization]; groupsDataSource.delegate = self; @@ -324,22 +350,25 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; }]; [self userInterfaceThemeDidChange]; + + self.signOutAlertPresenter = [SignOutAlertPresenter new]; + self.signOutAlertPresenter.delegate = self; } - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -350,7 +379,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)didReceiveMemoryWarning @@ -378,10 +407,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); documentInteractionController = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } if (isSavingInProgress || isResetPwdInProgress || is3PIDBindingInProgress) @@ -404,6 +433,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); [super destroy]; } + + keyBackupSetupCoordinatorBridgePresenter = nil; + keyBackupRecoverCoordinatorBridgePresenter = nil; } - (void)onMatrixSessionStateDidChange:(NSNotification *)notif @@ -773,8 +805,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { typeof(self) self = weakSelf; - UITextField *textField = [self->currentAlert textFields].firstObject; - NSString *smsCode = textField.text; + NSString *smsCode = [self->currentAlert textFields].firstObject.text; self->currentAlert = nil; @@ -955,30 +986,30 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Crypto information NSMutableAttributedString *cryptoInformationString = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_name", @"Vector", nil) - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont systemFontOfSize:17]}]; [cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:account.device.displayName ? account.device.displayName : @"" - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont systemFontOfSize:17]}]]; [cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_id", @"Vector", nil) - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont systemFontOfSize:17]}]]; [cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:account.device.deviceId ? account.device.deviceId : @"" - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont systemFontOfSize:17]}]]; [cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_crypto_device_key", @"Vector", nil) - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont systemFontOfSize:17]}]]; NSString *fingerprint = account.mxSession.crypto.deviceEd25519Key; [cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:fingerprint ? fingerprint : @"" - attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, + attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}]]; return cryptoInformationString; @@ -1224,7 +1255,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { if ([AppDelegate theDelegate].mxSessions.count > 0) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; count = session.ignoredUsers.count; } else @@ -1280,6 +1311,14 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); count = CRYPTOGRAPHY_COUNT; } } + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) + { + // Check whether this section is visible. + if (self.mainSession.crypto) + { + count = keyBackupSection.numberOfRows; + } + } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) { count = 1; @@ -1295,12 +1334,12 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); cell.mxkTextFieldLeadingConstraint.constant = 16; cell.mxkTextFieldTrailingConstraint.constant = 15; - cell.mxkLabel.textColor = kRiotPrimaryTextColor; + cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.mxkTextField.userInteractionEnabled = YES; cell.mxkTextField.borderStyle = UITextBorderStyleNone; cell.mxkTextField.textAlignment = NSTextAlignmentRight; - cell.mxkTextField.textColor = kRiotSecondaryTextColor; + cell.mxkTextField.textColor = ThemeService.shared.theme.textSecondaryColor; cell.mxkTextField.font = [UIFont systemFontOfSize:16]; cell.mxkTextField.placeholder = nil; @@ -1322,7 +1361,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left; cell.mxkSwitchTrailingConstraint.constant = 15; - cell.mxkLabel.textColor = kRiotPrimaryTextColor; + cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; [cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -1348,7 +1387,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); } cell.textLabel.accessibilityIdentifier = nil; cell.textLabel.font = [UIFont systemFontOfSize:17]; - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; return cell; } @@ -1357,7 +1396,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { MXKTableViewCellWithTextView *textViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier] forIndexPath:indexPath]; - textViewCell.mxkTextView.textColor = kRiotPrimaryTextColor; + textViewCell.mxkTextView.textColor = ThemeService.shared.theme.textPrimaryColor; textViewCell.mxkTextView.font = [UIFont systemFontOfSize:17]; textViewCell.mxkTextView.backgroundColor = [UIColor clearColor]; textViewCell.mxkTextViewLeadingConstraint.constant = tableView.separatorInset.left; @@ -1383,7 +1422,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); return cell; } - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject; if (section == SETTINGS_SECTION_SIGN_OUT_INDEX) @@ -1404,7 +1443,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); [signOutCell.mxkButton setTitle:title forState:UIControlStateNormal]; [signOutCell.mxkButton setTitle:title forState:UIControlStateHighlighted]; - [signOutCell.mxkButton setTintColor:kRiotColorGreen]; + [signOutCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; signOutCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [signOutCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -1436,7 +1475,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); profileCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_profile_picture", @"Vector", nil); profileCell.accessibilityIdentifier=@"SettingsVCProfilPictureStaticText"; - profileCell.mxkLabel.textColor = kRiotPrimaryTextColor; + profileCell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; // if the user defines a new avatar if (newAvatarImage) @@ -1527,12 +1566,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { newEmailCell.mxkLabel.text = nil; newEmailCell.mxkTextField.placeholder = NSLocalizedStringFromTable(@"settings_email_address_placeholder", @"Vector", nil); - if (kRiotPlaceholderTextColor) - { - newEmailCell.mxkTextField.attributedPlaceholder = [[NSAttributedString alloc] - initWithString:newEmailCell.mxkTextField.placeholder - attributes:@{NSForegroundColorAttributeName: kRiotPlaceholderTextColor}]; - } + newEmailCell.mxkTextField.attributedPlaceholder = [[NSAttributedString alloc] + initWithString:newEmailCell.mxkTextField.placeholder + attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}]; newEmailCell.mxkTextField.text = newEmailTextField.text; newEmailCell.mxkTextField.userInteractionEnabled = YES; newEmailCell.mxkTextField.keyboardType = UIKeyboardTypeEmailAddress; @@ -1559,7 +1595,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); newEmailTextField = newEmailCell.mxkTextField; } - UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:kRiotColorGreen]; + UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:ThemeService.shared.theme.tintColor]; newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage]; } @@ -1626,7 +1662,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); if (!countryCode) { // If none, consider the preferred locale - NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]; + NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[NSBundle mainBundle] preferredLocalizations][0]]; if ([local respondsToSelector:@selector(countryCode)]) { countryCode = local.countryCode; @@ -1652,7 +1688,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); newPhoneNumberCell = newPhoneCell; } - UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:kRiotColorGreen]; + UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:ThemeService.shared.theme.tintColor]; newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage]; cell = newPhoneCell; @@ -1672,7 +1708,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); else if (row == userSettingsNightModeSepIndex) { UITableViewCell *sepCell = [[UITableViewCell alloc] init]; - sepCell.backgroundColor = kRiotSecondaryBgColor; + sepCell.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; cell = sepCell; } @@ -1695,6 +1731,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_push_notif", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = account.isPushKitNotificationActive; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePushNotifications:) forControlEvents:UIControlEventTouchUpInside]; @@ -1706,16 +1743,18 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_show_decrypted_content", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showDecryptedContentInNotifications; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = account.isPushKitNotificationActive; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowDecodedContent:) forControlEvents:UIControlEventTouchUpInside]; + cell = labelAndSwitchCell; } else if (row == NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX) { MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView]; - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; globalInfoCell.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_global_settings_info", @"Vector", nil), appDisplayName]; globalInfoCell.textLabel.numberOfLines = 0; @@ -1730,6 +1769,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_pin_rooms_with_missed_notif", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithMissedNotif:) forControlEvents:UIControlEventTouchUpInside]; @@ -1740,7 +1780,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_pin_rooms_with_unread", @"Vector", nil); - labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome; + labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithUnread:) forControlEvents:UIControlEventTouchUpInside]; @@ -1754,6 +1795,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_callkit", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].isCallKitEnabled; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleCallKit:) forControlEvents:UIControlEventTouchUpInside]; @@ -1790,7 +1832,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:language]; languageDescription = [languageDescription capitalizedStringWithLocale:locale]; - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.textLabel.text = NSLocalizedStringFromTable(@"settings_ui_language", @"Vector", nil); cell.detailTextLabel.text = languageDescription; @@ -1827,7 +1869,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); @"Vector", nil); - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.textLabel.text = NSLocalizedStringFromTable(@"settings_ui_theme", @"Vector", nil); cell.detailTextLabel.text = i18nTheme; @@ -1858,6 +1900,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.numberOfLines = 0; labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_contacts_discover_matrix_users", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].syncLocalContacts; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLocalContactsSync:) forControlEvents:UIControlEventTouchUpInside]; @@ -1872,10 +1915,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); } NSString* countryCode = [[MXKAppSettings standardAppSettings] phonebookCountryCode]; - NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]; + NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[NSBundle mainBundle] preferredLocalizations][0]]; NSString *countryName = [local displayNameForKey:NSLocaleCountryCode value:countryCode]; - cell.textLabel.textColor = kRiotPrimaryTextColor; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; cell.textLabel.text = NSLocalizedStringFromTable(@"settings_contacts_phonebook_country", @"Vector", nil); cell.detailTextLabel.text = countryName; @@ -1966,6 +2009,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); sendCrashReportCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_send_crash_report", @"Vector", nil); sendCrashReportCell.mxkSwitch.on = RiotSettings.shared.enableCrashReport; + sendCrashReportCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; sendCrashReportCell.mxkSwitch.enabled = YES; [sendCrashReportCell.mxkSwitch addTarget:self action:@selector(toggleSendCrashReport:) forControlEvents:UIControlEventTouchUpInside]; @@ -1977,6 +2021,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); enableRageShakeCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_rageshake", @"Vector", nil); enableRageShakeCell.mxkSwitch.on = RiotSettings.shared.enableRageShake; + enableRageShakeCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; enableRageShakeCell.mxkSwitch.enabled = YES; [enableRageShakeCell.mxkSwitch addTarget:self action:@selector(toggleEnableRageShake:) forControlEvents:UIControlEventTouchUpInside]; @@ -1998,7 +2043,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSString *btnTitle = NSLocalizedStringFromTable(@"settings_mark_all_as_read", @"Vector", nil); [markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; [markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; - [markAllBtnCell.mxkButton setTintColor:kRiotColorGreen]; + [markAllBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; markAllBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [markAllBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2023,7 +2068,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSString *btnTitle = NSLocalizedStringFromTable(@"settings_clear_cache", @"Vector", nil); [clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; [clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; - [clearCacheBtnCell.mxkButton setTintColor:kRiotColorGreen]; + [clearCacheBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; clearCacheBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [clearCacheBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2048,7 +2093,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSString *btnTitle = NSLocalizedStringFromTable(@"settings_report_bug", @"Vector", nil); [reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; [reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; - [reportBugBtnCell.mxkButton setTintColor:kRiotColorGreen]; + [reportBugBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; reportBugBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [reportBugBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2068,6 +2113,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject; labelAndSwitchCell.mxkSwitch.on = account.mxSession.syncWithLazyLoadOfRoomMembers; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleSyncWithLazyLoadOfRoomMembers:) forControlEvents:UIControlEventTouchUpInside]; @@ -2079,6 +2125,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_create_conference_with_jitsi", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.createConferenceCallsWithJitsi; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleJitsiForConference:) forControlEvents:UIControlEventTouchUpInside]; @@ -2086,12 +2133,13 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); } else if (row == LABS_CRYPTO_INDEX) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_e2e_encryption", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = (nil != session.crypto); + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsEndToEndEncryption:) forControlEvents:UIControlEventTouchUpInside]; @@ -2165,6 +2213,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_crypto_blacklist_unverified_devices", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = account.mxSession.crypto.globalBlacklistUnverifiedDevices; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; labelAndSwitchCell.mxkSwitch.enabled = YES; [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleBlacklistUnverifiedDevices:) forControlEvents:UIControlEventTouchUpInside]; @@ -2186,7 +2235,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSString *btnTitle = NSLocalizedStringFromTable(@"settings_crypto_export", @"Vector", nil); [exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; [exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; - [exportKeysBtnCell.mxkButton setTintColor:kRiotColorGreen]; + [exportKeysBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; exportKeysBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [exportKeysBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2196,6 +2245,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); cell = exportKeysBtnCell; } } + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) + { + cell = [keyBackupSection cellForRowAtRow:row]; + } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) { MXKTableViewCellWithButton *deactivateAccountBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]]; @@ -2213,7 +2266,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); NSString *btnTitle = NSLocalizedStringFromTable(@"settings_deactivate_my_account", @"Vector", nil); [deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; [deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; - [deactivateAccountBtnCell.mxkButton setTintColor:kRiotColorRed]; + [deactivateAccountBtnCell.mxkButton setTintColor:ThemeService.shared.theme.warningColor]; deactivateAccountBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; [deactivateAccountBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; @@ -2252,7 +2305,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Check whether this section is visible if ([AppDelegate theDelegate].mxSessions.count > 0) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; if (session.ignoredUsers.count) { return NSLocalizedStringFromTable(@"settings_ignored_users", @"Vector", nil); @@ -2299,12 +2352,12 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil); } } - else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX) + else if (section == SETTINGS_SECTION_KEYBACKUP_INDEX) { // Check whether this section is visible if (self.mainSession.crypto) { - return NSLocalizedStringFromTable(@"settings_cryptography", @"Vector", nil); + return NSLocalizedStringFromTable(@"settings_key_backup", @"Vector", nil); } } else if (section == SETTINGS_SECTION_DEACTIVATE_ACCOUNT_INDEX) @@ -2321,7 +2374,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; - tableViewHeaderFooterView.textLabel.textColor = kRiotPrimaryTextColor; + tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; tableViewHeaderFooterView.textLabel.font = [UIFont systemFontOfSize:15]; } } @@ -2349,15 +2402,15 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; if (cell.selectionStyle != UITableViewCellSelectionStyleNone) { // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -2379,7 +2432,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { if ([AppDelegate theDelegate].mxSessions.count > 0) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; if (session.ignoredUsers.count == 0) { // Hide this section @@ -2411,7 +2464,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { if ([AppDelegate theDelegate].mxSessions.count > 0) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; if (session.ignoredUsers.count == 0) { // Hide this section @@ -2460,7 +2513,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); }]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_pink" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(50, cellHeight) resourceSize:CGSizeMake(24, 24)]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_pink" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(50, cellHeight) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:leaveAction atIndex:0]; } } @@ -2492,7 +2545,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); } else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; NSString *ignoredUserId; if (indexPath.row < session.ignoredUsers.count) @@ -2517,7 +2570,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); typeof(self) self = weakSelf; self->currentAlert = nil; - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; // Remove the member from the ignored user list [self startActivityIndicator]; @@ -2655,24 +2708,14 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); - (void)onSignout:(id)sender { - // Feedback: disable button and run activity indicator - UIButton *button = (UIButton*)sender; - button.enabled = NO; - [self startActivityIndicator]; + self.signOutButton = (UIButton*)sender; - __weak typeof(self) weakSelf = self; - - [[AppDelegate theDelegate] logoutWithConfirmation:YES completion:^(BOOL isLoggedOut) { - - if (!isLoggedOut && weakSelf) - { - typeof(self) self = weakSelf; - - // Enable the button and stop activity indicator - button.enabled = YES; - [self stopActivityIndicator]; - } - }]; + MXKeyBackupState backupState = self.mainSession.crypto.backup.state; + [self.signOutAlertPresenter + presentFor:backupState + from:self + sourceView:self.signOutButton + animated:YES]; } - (void)onRemove3PID:(NSIndexPath*)path @@ -2794,7 +2837,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); __weak typeof(self) weakSelf = self; - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; currentAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_on_denied_notification", @"Vector", nil), appDisplayName] message:nil preferredStyle:UIAlertControllerStyleAlert]; @@ -3048,7 +3091,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { [self startActivityIndicator]; - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; [session enableCrypto:switchButton.isOn success:^{ // When disabling crypto, reset the current device id as it cannot be reused. @@ -3478,7 +3521,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Dismiss the keyboard [newEmailTextField resignFirstResponder]; - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; MXK3PID *new3PID = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:newEmailTextField.text]; [new3PID requestValidationTokenWithMatrixRestClient:session.matrixRestClient isDuringRegistration:NO nextLink:nil success:^{ @@ -3569,7 +3612,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // Dismiss the keyboard [newPhoneNumberCell.mxkTextField resignFirstResponder]; - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:newPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil]; NSString *msisdn; @@ -3635,7 +3678,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); { if ([AppDelegate theDelegate].mxSessions.count > 0) { - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; + MXSession* session = [AppDelegate theDelegate].mxSessions[0]; MXMyUser* myUser = session.myUser; BOOL saveButtonEnabled = (nil != newAvatarImage); @@ -3765,6 +3808,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); // The user wants to select this theme RiotSettings.shared.userInterfaceTheme = newTheme; + ThemeService.shared.themeId = newTheme; [self.tableView reloadData]; } @@ -4196,7 +4240,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); - (void)deactivateAccountViewControllerDidDeactivateWithSuccess:(DeactivateAccountViewController *)deactivateAccountViewController { NSLog(@"[SettingsViewController] Deactivate account with success"); - [[AppDelegate theDelegate] logoutSendingRequestServer:NO completion:^(BOOL isLoggedOut) { NSLog(@"[SettingsViewController] Complete clear user data after account deactivation"); @@ -4208,4 +4251,229 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); [deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil]; } +#pragma mark - SettingsKeyBackupTableViewSectionDelegate + +- (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection +{ + [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:SETTINGS_SECTION_KEYBACKUP_INDEX] + withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +- (MXKTableViewCellWithTextView *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection textCellForRow:(NSInteger)textCellForRow +{ + return [self textViewCellForTableView:self.tableView atIndexPath:[NSIndexPath indexPathForRow:textCellForRow inSection:SETTINGS_SECTION_KEYBACKUP_INDEX]]; +} + +- (MXKTableViewCellWithButton *)settingsKeyBackupTableViewSection:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow +{ + MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]]; + + if (!cell) + { + cell = [[MXKTableViewCellWithButton alloc] init]; + } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + cell.mxkButton.titleLabel.text = nil; + } + + cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; + [cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor]; + + return cell; +} + +- (void)settingsKeyBackupTableViewSectionShowKeyBackupSetup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection +{ + [self showKeyBackupSetupFromSignOutFlow:NO]; +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion +{ + [self showKeyBackupRecover:keyBackupVersion]; +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion +{ + MXWeakify(self); + [currentAlert dismissViewControllerAnimated:NO completion:nil]; + + currentAlert = + [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil) + message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_button_delete", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + + [self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion]; + }]]; + + [currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"]; + [self presentViewController:currentAlert animated:YES completion:nil]; +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showActivityIndicator:(BOOL)show +{ + if (show) + { + [self startActivityIndicator]; + } + else + { + [self stopActivityIndicator]; + } +} + +- (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection showError:(NSError *)error +{ + [[AppDelegate theDelegate] showErrorAsAlert:error]; +} + +#pragma mark - MXKEncryptionInfoView + +- (void)showDeviceInfo:(MXDeviceInfo*)deviceInfo +{ + // Show it modally on the root view controller + // TODO: Improve it + UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController; + if (rootViewController) + { + EncryptionInfoView *encryptionInfoView = [[EncryptionInfoView alloc] initWithDeviceInfo:deviceInfo andMatrixSession:self.mainSession]; + [encryptionInfoView onButtonPressed:encryptionInfoView.verifyButton]; + + encryptionInfoView.delegate = self; + + // Add shadow on added view + encryptionInfoView.layer.cornerRadius = 5; + encryptionInfoView.layer.shadowOffset = CGSizeMake(0, 1); + encryptionInfoView.layer.shadowOpacity = 0.5f; + + // Add the view and define edge constraints + [rootViewController.view addSubview:encryptionInfoView]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:rootViewController.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0f + constant:10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:rootViewController.bottomLayoutGuide + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:-10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:rootViewController.view + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:encryptionInfoView + attribute:NSLayoutAttributeLeading + multiplier:1.0f + constant:-10.0f]]; + + [rootViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:rootViewController.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:encryptionInfoView + attribute:NSLayoutAttributeTrailing + multiplier:1.0f + constant:10.0f]]; + [rootViewController.view setNeedsUpdateConstraints]; + } +} + +- (void)encryptionInfoView:(MXKEncryptionInfoView*)encryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo*)deviceInfo +{ + [keyBackupSection reload]; +} + +#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter + +- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow +{ + keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + + [keyBackupSetupCoordinatorBridgePresenter presentFrom:self + isStartedFromSignOut:showFromSignOutFlow + animated:true]; + + keyBackupSetupCoordinatorBridgePresenter.delegate = self; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; + + [keyBackupSection reload]; +} + +#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter + +- (void)showKeyBackupRecover:(MXKeyBackupVersion*)keyBackupVersion +{ + keyBackupRecoverCoordinatorBridgePresenter = [[KeyBackupRecoverCoordinatorBridgePresenter alloc] initWithSession:self.mainSession keyBackupVersion:keyBackupVersion]; + + [keyBackupRecoverCoordinatorBridgePresenter presentFrom:self animated:true]; + keyBackupRecoverCoordinatorBridgePresenter.delegate = self; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidCancel:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupRecoverCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupRecoverCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupRecoverCoordinatorBridgePresenter = nil; +} + +#pragma mark - SignOutAlertPresenterDelegate + +- (void)signOutAlertPresenterDidTapBackupAction:(SignOutAlertPresenter * _Nonnull)presenter +{ + [self showKeyBackupSetupFromSignOutFlow:YES]; +} + +- (void)signOutAlertPresenterDidTapSignOutAction:(SignOutAlertPresenter * _Nonnull)presenter +{ + // Prevent user to perform user interaction in settings when sign out + // TODO: Prevent user interaction in all application (navigation controller and split view controller included) + self.view.userInteractionEnabled = NO; + self.signOutButton.enabled = NO; + + [self startActivityIndicator]; + + MXWeakify(self); + + [[AppDelegate theDelegate] logoutWithConfirmation:NO completion:^(BOOL isLoggedOut) { + MXStrongifyAndReturnIfNil(self); + + [self stopActivityIndicator]; + + self.view.userInteractionEnabled = YES; + self.signOutButton.enabled = YES; + }]; +} + @end diff --git a/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift b/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift new file mode 100644 index 000000000..a29a8bc5a --- /dev/null +++ b/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift @@ -0,0 +1,142 @@ +/* + 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 + +@objc protocol SignOutAlertPresenterDelegate: class { + func signOutAlertPresenterDidTapSignOutAction(_ presenter: SignOutAlertPresenter) + func signOutAlertPresenterDidTapBackupAction(_ presenter: SignOutAlertPresenter) +} + +@objcMembers +final class SignOutAlertPresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private weak var presentingViewController: UIViewController? + private weak var sourceView: UIView? + + // MARK: Public + + weak var delegate: SignOutAlertPresenterDelegate? + + // MARK: - Public + + func present(for keyBackupState: MXKeyBackupState, from viewController: UIViewController, sourceView: UIView?, animated: Bool) { + self.sourceView = sourceView + self.presentingViewController = viewController + + switch keyBackupState { + case MXKeyBackupStateUnknown, MXKeyBackupStateDisabled, MXKeyBackupStateCheckingBackUpOnHomeserver: + self.presentNonExistingBackupAlert(animated: animated) + case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: + self.presentBackupInProgressAlert(animated: animated) + default: + self.presentExistingBackupAlert(animated: animated) + } + } + + // MARK: - Private + + private func presentExistingBackupAlert(animated: Bool) { + let alertContoller = UIAlertController(title: VectorL10n.signOutExistingKeyBackupAlertTitle, + message: nil, + preferredStyle: .actionSheet) + + let signoutAction = UIAlertAction(title: VectorL10n.signOutExistingKeyBackupAlertSignOutAction, style: .destructive) { (_) in + self.delegate?.signOutAlertPresenterDidTapSignOutAction(self) + } + + let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: nil) + + alertContoller.addAction(signoutAction) + alertContoller.addAction(cancelAction) + + self.present(alertController: alertContoller, animated: animated) + } + + private func presentNonExistingBackupAlert(animated: Bool) { + let alertContoller = UIAlertController(title: VectorL10n.signOutNonExistingKeyBackupAlertTitle, + message: nil, + preferredStyle: .actionSheet) + + let doNotWantKeyBackupAction = UIAlertAction(title: VectorL10n.signOutNonExistingKeyBackupAlertDiscardKeyBackupAction, style: .destructive) { (_) in + self.presentNonExistingBackupSignOutConfirmationAlert(animated: true) + } + + let setUpKeyBackupAction = UIAlertAction(title: VectorL10n.signOutNonExistingKeyBackupAlertSetupKeyBackupAction, style: .default) { (_) in + self.delegate?.signOutAlertPresenterDidTapBackupAction(self) + } + + let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: nil) + + alertContoller.addAction(doNotWantKeyBackupAction) + alertContoller.addAction(setUpKeyBackupAction) + alertContoller.addAction(cancelAction) + + self.present(alertController: alertContoller, animated: animated) + } + + private func presentNonExistingBackupSignOutConfirmationAlert(animated: Bool) { + let alertContoller = UIAlertController(title: VectorL10n.signOutNonExistingKeyBackupSignOutConfirmationAlertTitle, + message: VectorL10n.signOutNonExistingKeyBackupSignOutConfirmationAlertMessage, + preferredStyle: .alert) + + let signOutAction = UIAlertAction(title: VectorL10n.signOutNonExistingKeyBackupSignOutConfirmationAlertSignOutAction, style: .destructive) { (_) in + self.delegate?.signOutAlertPresenterDidTapSignOutAction(self) + } + + let setUpKeyBackupAction = UIAlertAction(title: VectorL10n.signOutNonExistingKeyBackupSignOutConfirmationAlertBackupAction, style: .default) { (_) in + self.delegate?.signOutAlertPresenterDidTapBackupAction(self) + } + + alertContoller.addAction(signOutAction) + alertContoller.addAction(setUpKeyBackupAction) + + self.present(alertController: alertContoller, animated: animated) + } + + private func presentBackupInProgressAlert(animated: Bool) { + let alertContoller = UIAlertController(title: VectorL10n.signOutKeyBackupInProgressAlertTitle, + message: nil, + preferredStyle: .actionSheet) + + let discardKeyBackupAction = UIAlertAction(title: VectorL10n.signOutKeyBackupInProgressAlertDiscardKeyBackupAction, style: .destructive) { (_) in + self.delegate?.signOutAlertPresenterDidTapSignOutAction(self) + } + + let cancelAction = UIAlertAction(title: VectorL10n.signOutKeyBackupInProgressAlertCancelAction, style: .cancel, handler: nil) + + alertContoller.addAction(discardKeyBackupAction) + alertContoller.addAction(cancelAction) + + self.present(alertController: alertContoller, animated: animated) + } + + private func present(alertController: UIAlertController, animated: Bool) { + + // Configure source view when alert controller is presented with a popover + if let sourceView = self.sourceView, let popoverPresentationController = alertController.popoverPresentationController { + popoverPresentationController.sourceView = sourceView + popoverPresentationController.sourceRect = sourceView.bounds + popoverPresentationController.permittedArrowDirections = [.down, .up] + } + + self.presentingViewController?.present(alertController, animated: animated, completion: nil) + } +} diff --git a/Riot/Modules/Settings/Views/DeviceView.m b/Riot/Modules/Settings/Views/DeviceView.m index 27ba9245b..7f1325990 100644 --- a/Riot/Modules/Settings/Views/DeviceView.m +++ b/Riot/Modules/Settings/Views/DeviceView.m @@ -17,7 +17,8 @@ #import "DeviceView.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" @implementation DeviceView @@ -27,12 +28,12 @@ { [super customizeViewRendering]; - self.containerView.backgroundColor = kRiotSecondaryBgColor; - self.textView.backgroundColor = kRiotPrimaryBgColor; - self.defaultTextColor = kRiotPrimaryTextColor; - self.cancelButton.tintColor = kRiotColorGreen; - self.deleteButton.tintColor = kRiotColorGreen; - self.renameButton.tintColor = kRiotColorGreen; + self.containerView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + self.textView.backgroundColor = ThemeService.shared.theme.backgroundColor; + self.defaultTextColor = ThemeService.shared.theme.textPrimaryColor; + self.cancelButton.tintColor = ThemeService.shared.theme.tintColor; + self.deleteButton.tintColor = ThemeService.shared.theme.tintColor; + self.renameButton.tintColor = ThemeService.shared.theme.tintColor; } @end diff --git a/Riot/Modules/Settings/Views/TableViewCellWithPhoneNumberTextField.m b/Riot/Modules/Settings/Views/TableViewCellWithPhoneNumberTextField.m index 3f9d7e012..9db346839 100644 --- a/Riot/Modules/Settings/Views/TableViewCellWithPhoneNumberTextField.m +++ b/Riot/Modules/Settings/Views/TableViewCellWithPhoneNumberTextField.m @@ -16,7 +16,8 @@ #import "TableViewCellWithPhoneNumberTextField.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "NBPhoneNumberUtil.h" @@ -26,10 +27,10 @@ { [super customizeTableViewCellRendering]; - self.mxkLabel.textColor = kRiotPrimaryTextColor; - self.mxkTextField.textColor = kRiotPrimaryTextColor; + self.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + self.mxkTextField.textColor = ThemeService.shared.theme.textPrimaryColor; - _isoCountryCodeLabel.textColor = kRiotPrimaryTextColor; + _isoCountryCodeLabel.textColor = ThemeService.shared.theme.textPrimaryColor; } - (void)setIsoCountryCode:(NSString *)isoCountryCode diff --git a/Riot/Modules/StartChat/StartChatViewController.m b/Riot/Modules/StartChat/StartChatViewController.m index d55a50f60..cbc7dabf4 100644 --- a/Riot/Modules/StartChat/StartChatViewController.m +++ b/Riot/Modules/StartChat/StartChatViewController.m @@ -18,6 +18,7 @@ #import "StartChatViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface StartChatViewController () { @@ -148,10 +149,10 @@ [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; + _searchBarHeaderBorder.backgroundColor = ThemeService.shared.theme.headerBorderColor; // Check the table view style to select its bg color. - self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.contactsTableView.backgroundColor; if (self.contactsTableView.dataSource) @@ -162,7 +163,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy @@ -257,7 +258,7 @@ if (identifiers.count) { // Here the contact can only have one identifier - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:identifiers.firstObject]; + contactsDataSource.ignoredContactsByMatrixId[identifiers.firstObject] = contact; } else { @@ -266,7 +267,7 @@ { // Here the contact can only have one email MXKEmail *email = emails.firstObject; - [contactsDataSource.ignoredContactsByEmail setObject:contact forKey:email.emailAddress]; + contactsDataSource.ignoredContactsByEmail[email.emailAddress] = contact; } } isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO)); @@ -274,7 +275,7 @@ if (userContact) { - [contactsDataSource.ignoredContactsByMatrixId setObject:userContact forKey:self.mainSession.myUser.userId]; + contactsDataSource.ignoredContactsByMatrixId[self.mainSession.myUser.userId] = userContact; } } @@ -380,13 +381,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { @@ -452,7 +453,7 @@ }]; - leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:kRiotSecondaryBgColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; + leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)]; [actions insertObject:leaveAction atIndex:0]; } @@ -620,19 +621,19 @@ - (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar { // bar tint color - searchBar.barTintColor = searchBar.tintColor = kRiotColorGreen; - searchBar.tintColor = kRiotColorGreen; + searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.theme.tintColor; + searchBar.tintColor = ThemeService.shared.theme.tintColor; // FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals. // text color UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"]; - searchBarTextField.textColor = kRiotSecondaryTextColor; + searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor; // Magnifying glass icon. UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView; leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - leftImageView.tintColor = kRiotColorGreen; + leftImageView.tintColor = ThemeService.shared.theme.tintColor; // remove the gray background color UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"]; @@ -645,8 +646,8 @@ { searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), - NSUnderlineColorAttributeName: kRiotColorGreen, - NSForegroundColorAttributeName: kRiotColorGreen}]; + NSUnderlineColorAttributeName: ThemeService.shared.theme.tintColor, + NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}]; } } diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m index 1190e2ee6..d926c8781 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.m +++ b/Riot/Modules/TabBar/MasterTabBarController.m @@ -55,8 +55,8 @@ // Keep reference on the pushed view controllers to release them correctly NSMutableArray *childViewControllers; - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; // The groups data source GroupsDataSource *groupsDataSource; @@ -74,11 +74,11 @@ // Do any additional setup after loading the view, typically from a nib. // Retrieve the all view controllers - _homeViewController = [self.viewControllers objectAtIndex:TABBAR_HOME_INDEX]; - _favouritesViewController = [self.viewControllers objectAtIndex:TABBAR_FAVOURITES_INDEX]; - _peopleViewController = [self.viewControllers objectAtIndex:TABBAR_PEOPLE_INDEX]; - _roomsViewController = [self.viewControllers objectAtIndex:TABBAR_ROOMS_INDEX]; - _groupsViewController = [self.viewControllers objectAtIndex:TABBAR_GROUPS_INDEX]; + _homeViewController = self.viewControllers[TABBAR_HOME_INDEX]; + _favouritesViewController = self.viewControllers[TABBAR_FAVOURITES_INDEX]; + _peopleViewController = self.viewControllers[TABBAR_PEOPLE_INDEX]; + _roomsViewController = self.viewControllers[TABBAR_ROOMS_INDEX]; + _groupsViewController = self.viewControllers[TABBAR_GROUPS_INDEX]; // Set the accessibility labels for all buttons #1842 [_settingsBarButtonItem setAccessibilityLabel:NSLocalizedStringFromTable(@"settings_title", @"Vector", nil)]; @@ -104,7 +104,7 @@ [self initializeDataSources]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -114,17 +114,19 @@ - (void)userInterfaceThemeDidChange { - self.tabBar.tintColor = kRiotColorGreen; - self.tabBar.barTintColor = kRiotSecondaryBgColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.tabBar.tintColor = ThemeService.shared.theme.tintColor; + self.tabBar.barTintColor = ThemeService.shared.theme.headerBackgroundColor; - self.view.backgroundColor = kRiotPrimaryBgColor; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; [self setNeedsStatusBarAppearanceUpdate]; } - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)viewWillAppear:(BOOL)animated @@ -216,10 +218,10 @@ authViewControllerObserver = nil; } - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } childViewControllers = nil; @@ -784,7 +786,7 @@ { _hidden = hidden; - [self.view superview].backgroundColor = kRiotPrimaryBgColor; + [self.view superview].backgroundColor = ThemeService.shared.theme.backgroundColor; self.view.hidden = hidden; self.navigationController.navigationBar.hidden = hidden; } @@ -794,11 +796,17 @@ - (void)refreshTabBarBadges { // Use a middle dot to signal missed notif in favourites - [self setMissedDiscussionsMark:(recentsDataSource.missedFavouriteDiscussionsCount? @"\u00B7": nil) onTabBarItem:TABBAR_FAVOURITES_INDEX withBadgeColor:(recentsDataSource.missedHighlightFavouriteDiscussionsCount ? kRiotColorPinkRed : kRiotColorGreen)]; + [self setMissedDiscussionsMark:(recentsDataSource.missedFavouriteDiscussionsCount? @"\u00B7": nil) + onTabBarItem:TABBAR_FAVOURITES_INDEX + withBadgeColor:(recentsDataSource.missedHighlightFavouriteDiscussionsCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor)]; // Update the badge on People and Rooms tabs - [self setMissedDiscussionsCount:recentsDataSource.missedDirectDiscussionsCount onTabBarItem:TABBAR_PEOPLE_INDEX withBadgeColor:(recentsDataSource.missedHighlightDirectDiscussionsCount ? kRiotColorPinkRed : kRiotColorGreen)]; - [self setMissedDiscussionsCount:recentsDataSource.missedGroupDiscussionsCount onTabBarItem:TABBAR_ROOMS_INDEX withBadgeColor:(recentsDataSource.missedHighlightGroupDiscussionsCount ? kRiotColorPinkRed : kRiotColorGreen)]; + [self setMissedDiscussionsCount:recentsDataSource.missedDirectDiscussionsCount + onTabBarItem:TABBAR_PEOPLE_INDEX + withBadgeColor:(recentsDataSource.missedHighlightDirectDiscussionsCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor)]; + [self setMissedDiscussionsCount:recentsDataSource.missedGroupDiscussionsCount + onTabBarItem:TABBAR_ROOMS_INDEX + withBadgeColor:(recentsDataSource.missedHighlightGroupDiscussionsCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor)]; } - (void)setMissedDiscussionsCount:(NSUInteger)count onTabBarItem:(NSUInteger)index withBadgeColor:(UIColor*)badgeColor @@ -809,7 +817,7 @@ self.tabBar.items[index].badgeValue = badgeValue; - if ([UITabBarItem instancesRespondToSelector:@selector(setBadgeColor:)]) + if (@available(iOS 10, *)) { self.tabBar.items[index].badgeColor = badgeColor; } @@ -826,7 +834,7 @@ { self.tabBar.items[index].badgeValue = mark; - if ([UITabBarItem instancesRespondToSelector:@selector(setBadgeColor:)]) + if (@available(iOS 10, *)) { self.tabBar.items[index].badgeColor = badgeColor; } @@ -864,7 +872,7 @@ [currentAlert dismissViewControllerAnimated:NO completion:nil]; - NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; currentAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"google_analytics_use_prompt", @"Vector", nil), appDisplayName] message:nil preferredStyle:UIAlertControllerStyleAlert]; diff --git a/Riot/Modules/UserDevices/UsersDevicesViewController.m b/Riot/Modules/UserDevices/UsersDevicesViewController.m index b2847f6ac..132627553 100644 --- a/Riot/Modules/UserDevices/UsersDevicesViewController.m +++ b/Riot/Modules/UserDevices/UsersDevicesViewController.m @@ -18,6 +18,7 @@ #import "UsersDevicesViewController.h" #import "AppDelegate.h" +#import "Riot-Swift.h" @interface UsersDevicesViewController () { @@ -26,8 +27,8 @@ void (^onCompleteBlock)(BOOL doneButtonPressed); - // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. - id kRiotDesignValuesDidChangeThemeNotificationObserver; + // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. + id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -71,7 +72,7 @@ self.tableView.tableFooterView = [[UIView alloc] init]; // Observe user interface theme change. - kRiotDesignValuesDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kRiotDesignValuesDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { [self userInterfaceThemeDidChange]; @@ -81,12 +82,12 @@ - (void)userInterfaceThemeDidChange { - self.defaultBarTintColor = kRiotSecondaryBgColor; - self.barTitleColor = kRiotPrimaryTextColor; - self.activityIndicator.backgroundColor = kRiotOverlayColor; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + + self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor; // Check the table view style to select its bg color. - self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); + self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor); self.view.backgroundColor = self.tableView.backgroundColor; if (self.tableView.dataSource) @@ -97,17 +98,17 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - return kRiotDesignStatusBarStyle; + return ThemeService.shared.theme.statusBarStyle; } - (void)destroy { [super destroy]; - if (kRiotDesignValuesDidChangeThemeNotificationObserver) + if (kThemeServiceDidChangeThemeNotificationObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:kRiotDesignValuesDidChangeThemeNotificationObserver]; - kRiotDesignValuesDidChangeThemeNotificationObserver = nil; + [[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver]; + kThemeServiceDidChangeThemeNotificationObserver = nil; } } @@ -177,13 +178,13 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; { - cell.backgroundColor = kRiotPrimaryBgColor; + cell.backgroundColor = ThemeService.shared.theme.backgroundColor; // Update the selected background view - if (kRiotSelectedBgColor) + if (ThemeService.shared.theme.selectedBackgroundColor) { cell.selectedBackgroundView = [[UIView alloc] init]; - cell.selectedBackgroundView.backgroundColor = kRiotSelectedBgColor; + cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor; } else { diff --git a/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m b/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m index 267c5a6bb..9ee73a778 100644 --- a/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m +++ b/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m @@ -17,7 +17,9 @@ #import "DeviceTableViewCell.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" + #import "MXRoom+Riot.h" #define DEVICE_TABLEVIEW_ROW_CELL_HEIGHT_WITHOUT_LABEL_HEIGHT 59 @@ -30,15 +32,15 @@ { [super customizeTableViewCellRendering]; - self.deviceName.textColor = kRiotPrimaryTextColor; + self.deviceName.textColor = ThemeService.shared.theme.textPrimaryColor; [self.verifyButton.layer setCornerRadius:5]; self.verifyButton.clipsToBounds = YES; - self.verifyButton.backgroundColor = kRiotColorGreen; + self.verifyButton.backgroundColor = ThemeService.shared.theme.tintColor; [self.blockButton.layer setCornerRadius:5]; self.blockButton.clipsToBounds = YES; - self.blockButton.backgroundColor = kRiotColorGreen; + self.blockButton.backgroundColor = ThemeService.shared.theme.tintColor; } - (void)render:(MXDeviceInfo *)deviceInfo diff --git a/Riot/Routers/NavigationRouter.swift b/Riot/Routers/NavigationRouter.swift new file mode 100755 index 000000000..799de7380 --- /dev/null +++ b/Riot/Routers/NavigationRouter.swift @@ -0,0 +1,120 @@ +/* + 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 + +/// `NavigationRouter` is a concrete implementation of NavigationRouterType. +final class NavigationRouter: NSObject, NavigationRouterType { + + // MARK: - Properties + + // MARK: Private + + private var completions: [UIViewController : () -> Void] + + // MARK: Public + + private let navigationController: UINavigationController + + // MARK: - Setup + + init(navigationController: UINavigationController = UINavigationController()) { + self.navigationController = navigationController + self.completions = [:] + super.init() + self.navigationController.delegate = self + } + + // MARK: - Public + + func present(_ module: Presentable, animated: Bool = true) { + navigationController.present(module.toPresentable(), animated: animated, completion: nil) + } + + func dismissModule(animated: Bool = true, completion: (() -> Void)? = nil) { + navigationController.dismiss(animated: animated, completion: completion) + } + + func setRootModule(_ module: Presentable, hideNavigationBar: Bool = false) { + // Call all completions so all coordinators can be deallocated + completions.forEach { $0.value() } + navigationController.setViewControllers([module.toPresentable()], animated: false) + navigationController.isNavigationBarHidden = hideNavigationBar + } + + func popToRootModule(animated: Bool) { + if let controllers = navigationController.popToRootViewController(animated: animated) { + controllers.forEach { runCompletion(for: $0) } + } + } + + func popToModule(_ module: Presentable, animated: Bool) { + if let controllers = navigationController.popToViewController(module.toPresentable(), animated: animated) { + controllers.forEach { runCompletion(for: $0) } + } + } + + func push(_ module: Presentable, animated: Bool = true, popCompletion: (() -> Void)? = nil) { + + let controller = module.toPresentable() + + // Avoid pushing UINavigationController onto stack + guard controller is UINavigationController == false else { + return + } + + if let completion = popCompletion { + completions[controller] = completion + } + + navigationController.pushViewController(controller, animated: animated) + } + + func popModule(animated: Bool = true) { + if let controller = navigationController.popViewController(animated: animated) { + runCompletion(for: controller) + } + } + + // MARK: Presentable + + func toPresentable() -> UIViewController { + return navigationController + } + + // MARK: - Private + + private func runCompletion(for controller: UIViewController) { + guard let completion = completions[controller] else { return } + completion() + completions.removeValue(forKey: controller) + } +} + +// MARK: - UINavigationControllerDelegate +extension NavigationRouter: UINavigationControllerDelegate { + + func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { + + // Ensure the view controller is popping + guard let poppedViewController = navigationController.transitionCoordinator?.viewController(forKey: .from), + !navigationController.viewControllers.contains(poppedViewController) else { + return + } + + runCompletion(for: poppedViewController) + } +} diff --git a/Riot/Routers/NavigationRouterType.swift b/Riot/Routers/NavigationRouterType.swift new file mode 100755 index 000000000..a75952001 --- /dev/null +++ b/Riot/Routers/NavigationRouterType.swift @@ -0,0 +1,69 @@ +/* + 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 + +/// Protocol describing a router that wraps a UINavigationController and add convenient completion handlers. Completions are called when a Presentable is removed. +/// Routers are used to be passed between coordinators. They handles only `physical` navigation. +protocol NavigationRouterType: class, Presentable { + + /// Present modally a view controller on the navigation controller + /// + /// - Parameter module: The Presentable to present. + /// - Parameter animated: Specify true to animate the transition. + func present(_ module: Presentable, animated: Bool) + + /// Dismiss presented view controller from navigation controller + /// + /// - Parameter animated: Specify true to animate the transition. + /// - Parameter completion: Animation completion (not the pop completion). + func dismissModule(animated: Bool, completion: (() -> Void)?) + + /// Set root view controller of navigation controller + /// + /// - Parameter module: The Presentable to set as root. + /// - Parameter hideNavigationBar: Specify true to hide the UINavigationBar. + func setRootModule(_ module: Presentable, hideNavigationBar: Bool) + + /// Pop to root view controller of navigation controller and remove all others + /// + /// - Parameter animated: Specify true to animate the transition. + func popToRootModule(animated: Bool) + + /// Pops view controllers until the specified view controller is at the top of the navigation stack + /// + /// - Parameter module: The Presentable that should to be at the top of the stack. + /// - Parameter animated: Specify true to animate the transition. + func popToModule(_ module: Presentable, animated: Bool) + + /// Push a view controller on navigation controller stack + /// + /// - Parameter animated: Specify true to animate the transition. + /// - Parameter popCompletion: Completion called when `module` is removed from the navigation stack. + func push(_ module: Presentable, animated: Bool, popCompletion: (() -> Void)?) + + /// Pop last view controller from navigation controller stack + /// + /// - Parameter animated: Specify true to animate the transition. + func popModule(animated: Bool) +} + +// `NavigationRouterType` default implementation +extension NavigationRouterType { + func setRootModule(_ module: Presentable) { + setRootModule(module, hideNavigationBar: false) + } +} diff --git a/Riot/Routers/Presentable.swift b/Riot/Routers/Presentable.swift new file mode 100755 index 000000000..fd42d3059 --- /dev/null +++ b/Riot/Routers/Presentable.swift @@ -0,0 +1,28 @@ +/* + 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 + +/// Protocol used to pass UIViewControllers to routers +protocol Presentable { + func toPresentable() -> UIViewController +} + +extension UIViewController: Presentable { + public func toPresentable() -> UIViewController { + return self + } +} diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index 394933f9c..5a506aeca 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.7.11 + 0.8.0 CFBundleSignature ???? CFBundleVersion - 0.7.11 + 0.8.0 ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index 9dd6fab82..3d91a54f2 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -7,5 +7,6 @@ #import "WebViewViewController.h" #import "RiotNavigationController.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import "TableViewCellWithCheckBoxAndLabel.h" +#import "RecentsDataSource.h" diff --git a/Riot/SupportingFiles/Riot.entitlements b/Riot/SupportingFiles/Riot.entitlements index 779cd8dff..d65fe32b0 100644 --- a/Riot/SupportingFiles/Riot.entitlements +++ b/Riot/SupportingFiles/Riot.entitlements @@ -12,8 +12,22 @@ applinks:www.riot.im webcredentials:riot.im + com.apple.developer.icloud-container-identifiers + + iCloud.$(CFBundleIdentifier) + + com.apple.developer.icloud-services + + CloudDocuments + com.apple.developer.siri + com.apple.developer.ubiquity-container-identifiers + + iCloud.$(CFBundleIdentifier) + + com.apple.developer.ubiquity-kvstore-identifier + $(TeamIdentifierPrefix)$(CFBundleIdentifier) com.apple.security.application-groups group.im.vector diff --git a/Riot/Utils/AvatarGenerator.m b/Riot/Utils/AvatarGenerator.m index 3d8bcd1eb..dc0162ef3 100644 --- a/Riot/Utils/AvatarGenerator.m +++ b/Riot/Utils/AvatarGenerator.m @@ -17,7 +17,13 @@ #import "AvatarGenerator.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" + +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif @implementation AvatarGenerator @@ -33,10 +39,7 @@ static UILabel* backgroundLabel = nil; { if (!colorsList) { - colorsList = [[NSMutableArray alloc] init]; - [colorsList addObject:kRiotColorGreen]; - [colorsList addObject:kRiotColorLightGreen]; - [colorsList addObject:kRiotColorLightOrange]; + colorsList = ThemeService.shared.theme.avatarColors; } } @@ -90,7 +93,7 @@ static UILabel* backgroundLabel = nil; if (!backgroundLabel) { backgroundLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; - backgroundLabel.textColor = kRiotPrimaryBgColor; + backgroundLabel.textColor = ThemeService.shared.theme.backgroundColor; backgroundLabel.textAlignment = NSTextAlignmentCenter; backgroundLabel.font = [UIFont boldSystemFontOfSize:25]; } @@ -120,7 +123,7 @@ static UILabel* backgroundLabel = nil; + (UIImage *)imageFromText:(NSString*)text withBackgroundColor:(UIColor*)color size:(CGFloat)size andFontSize:(CGFloat)fontSize { UILabel *bgLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size, size)]; - bgLabel.textColor = kRiotPrimaryBgColor; + bgLabel.textColor = ThemeService.shared.theme.backgroundColor; bgLabel.textAlignment = NSTextAlignmentCenter; bgLabel.font = [UIFont boldSystemFontOfSize:fontSize]; @@ -164,12 +167,12 @@ static UILabel* backgroundLabel = nil; imageByKeyDict = [[NSMutableDictionary alloc] init]; } - UIImage* image = [imageByKeyDict objectForKey:key]; + UIImage* image = imageByKeyDict[key]; if (!image) { - image = [AvatarGenerator imageFromText:firstChar withBackgroundColor:[colorsList objectAtIndex:colorIndex]]; - [imageByKeyDict setObject:image forKey:key]; + image = [AvatarGenerator imageFromText:firstChar withBackgroundColor:colorsList[colorIndex]]; + imageByKeyDict[key] = image; } return image; @@ -190,7 +193,7 @@ static UILabel* backgroundLabel = nil; NSString* firstChar = [AvatarGenerator firstChar:(displayname ? displayname : itemId)]; NSUInteger colorIndex = [AvatarGenerator colorIndexForText:itemId]; - return [AvatarGenerator imageFromText:firstChar withBackgroundColor:[colorsList objectAtIndex:colorIndex] size:size andFontSize:fontSize]; + return [AvatarGenerator imageFromText:firstChar withBackgroundColor:colorsList[colorIndex] size:size andFontSize:fontSize]; } + (void)clear diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 33cf6d1b3..44d3feac1 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -17,7 +17,8 @@ #import "EventFormatter.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" +#import "Riot-Swift.h" #import "WidgetManager.h" @@ -205,7 +206,7 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; // Use the secondary bg color to set the background color in the default CSS. - NSUInteger bgColor = [MXKTools rgbValueWithColor:kRiotSecondaryBgColor]; + NSUInteger bgColor = [MXKTools rgbValueWithColor:ThemeService.shared.theme.headerBackgroundColor]; self.defaultCSS = [NSString stringWithFormat:@" \ pre,code { \ background-color: #%06lX; \ @@ -216,13 +217,13 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; font-size: small; \ }", (unsigned long)bgColor]; - self.defaultTextColor = kRiotPrimaryTextColor; - self.subTitleTextColor = kRiotSecondaryTextColor; - self.prefixTextColor = kRiotSecondaryTextColor; - self.bingTextColor = kRiotColorPinkRed; - self.encryptingTextColor = kRiotColorGreen; - self.sendingTextColor = kRiotSecondaryTextColor; - self.errorTextColor = kRiotColorRed; + self.defaultTextColor = ThemeService.shared.theme.textPrimaryColor; + self.subTitleTextColor = ThemeService.shared.theme.textSecondaryColor; + self.prefixTextColor = ThemeService.shared.theme.textSecondaryColor; + self.bingTextColor = ThemeService.shared.theme.noticeColor; + self.encryptingTextColor = ThemeService.shared.theme.tintColor; + self.sendingTextColor = ThemeService.shared.theme.textSecondaryColor; + self.errorTextColor = ThemeService.shared.theme.warningColor; self.defaultTextFont = [UIFont systemFontOfSize:15]; self.prefixTextFont = [UIFont boldSystemFontOfSize:15]; @@ -348,16 +349,13 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; - (NSAttributedString*)roomCreatePredecessorAttributedStringWithPredecessorRoomId:(NSString*)predecessorRoomId { - NSString *predecessorRoomPermalink = [MXTools permalinkToRoom:predecessorRoomId]; - NSDictionary *roomPredecessorReasonAttributes = @{ NSFontAttributeName : self.defaultTextFont }; NSDictionary *roomLinkAttributes = @{ NSFontAttributeName : self.defaultTextFont, - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle), - NSLinkAttributeName : predecessorRoomPermalink, + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) }; NSMutableAttributedString *roomPredecessorAttributedString = [NSMutableAttributedString new]; diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 182a9481c..7816130e0 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -149,6 +149,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) - (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)(NSError *error))failureBlock { + [self resetPendingData]; + NSString *UTTypeText = (__bridge NSString *)kUTTypeText; NSString *UTTypeURL = (__bridge NSString *)kUTTypeURL; NSString *UTTypeImage = (__bridge NSString *)kUTTypeImage; @@ -156,9 +158,32 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) NSString *UTTypeFileUrl = (__bridge NSString *)kUTTypeFileURL; NSString *UTTypeMovie = (__bridge NSString *)kUTTypeMovie; - __weak typeof(self) weakSelf = self; + BOOL areAllAttachmentsImages = [self areAllAttachmentsImages]; + NSMutableArray *pendingImagesItemProviders = [NSMutableArray new]; // Used to keep NSItemProvider associated to pending images (used only when all items are images). + + __block NSError *firstRequestError = nil; + __block NSMutableArray *returningExtensionItems = [NSMutableArray new]; + dispatch_group_t requestsGroup = dispatch_group_create(); - [self resetPendingData]; + void (^requestSuccess)(NSExtensionItem*) = ^(NSExtensionItem *extensionItem) { + if (extensionItem && ![returningExtensionItems containsObject:extensionItem]) + { + [returningExtensionItems addObject:extensionItem]; + } + + dispatch_group_leave(requestsGroup); + }; + + void (^requestFailure)(NSError*) = ^(NSError *requestError) { + if (requestError && !firstRequestError) + { + firstRequestError = requestError; + } + + dispatch_group_leave(requestsGroup); + }; + + __weak typeof(self) weakSelf = self; for (NSExtensionItem *item in self.shareExtensionContext.inputItems) { @@ -166,6 +191,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) { if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeFileUrl]) { + dispatch_group_enter(requestsGroup); + [itemProvider loadItemForTypeIdentifier:UTTypeFileUrl options:nil completionHandler:^(NSURL *fileUrl, NSError * _Null_unspecified error) { // Switch back on the main thread to handle correctly the UI change @@ -174,7 +201,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (weakSelf) { typeof(self) self = weakSelf; - [self sendFileWithUrl:fileUrl toRoom:room extensionItem:item failureBlock:failureBlock]; + [self sendFileWithUrl:fileUrl + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; } }); @@ -183,6 +214,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeText]) { + dispatch_group_enter(requestsGroup); + [itemProvider loadItemForTypeIdentifier:UTTypeText options:nil completionHandler:^(NSString *text, NSError * _Null_unspecified error) { // Switch back on the main thread to handle correctly the UI change @@ -191,7 +224,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (weakSelf) { typeof(self) self = weakSelf; - [self sendText:text toRoom:room extensionItem:item failureBlock:failureBlock]; + [self sendText:text + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; } }); @@ -200,6 +237,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeURL]) { + dispatch_group_enter(requestsGroup); + [itemProvider loadItemForTypeIdentifier:UTTypeURL options:nil completionHandler:^(NSURL *url, NSError * _Null_unspecified error) { // Switch back on the main thread to handle correctly the UI change @@ -208,7 +247,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (weakSelf) { typeof(self) self = weakSelf; - [self sendText:url.absoluteString toRoom:room extensionItem:item failureBlock:failureBlock]; + [self sendText:url.absoluteString + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; } }); @@ -217,6 +260,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeImage]) { + dispatch_group_enter(requestsGroup); + itemProvider.isLoaded = NO; [itemProvider loadItemForTypeIdentifier:UTTypeImage options:nil completionHandler:^(id _Nullable itemProviderItem, NSError * _Null_unspecified error) @@ -239,28 +284,62 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([(NSObject *)itemProviderItem isKindOfClass:[UIImage class]]) { + // An application can share directly an UIImage. + // The most common case is screenshot sharing without saving to file. + // As screenshot using PNG format when they are saved to file we also use PNG format when saving UIImage to NSData. UIImage *image = (UIImage*)itemProviderItem; - imageData = UIImageJPEGRepresentation(image, 1.0); + imageData = UIImagePNGRepresentation(image); } if (imageData) { - [self.pendingImages addObject:imageData]; + if (areAllAttachmentsImages) + { + [self.pendingImages addObject:imageData]; + [pendingImagesItemProviders addObject:itemProvider]; + } + else + { + CGSize imageSize = [self imageSizeFromImageData:imageData]; + self.imageCompressionMode = ImageCompressionModeNone; + self.actualLargeSize = MAX(imageSize.width, imageSize.height); + + [self sendImageData:imageData + withProvider:itemProvider + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; + } } else { NSLog(@"[ShareExtensionManager] sendContentToRoom: failed to loadItemForTypeIdentifier. Error: %@", error); + dispatch_group_leave(requestsGroup); } - if ([self areAttachmentsFullyLoaded]) + // Only prompt for image resize only if all items are images + if (areAllAttachmentsImages) { - UIAlertController *compressionPrompt = [self compressionPromptForPendingImagesWithShareBlock:^{ - [self sendImages:self.pendingImages withProviders:item.attachments toRoom:room extensionItem:item failureBlock:failureBlock]; - }]; - - if (compressionPrompt) + if ([self areAttachmentsFullyLoaded]) { - [self.delegate shareExtensionManager:self showImageCompressionPrompt:compressionPrompt]; + UIAlertController *compressionPrompt = [self compressionPromptForPendingImagesWithShareBlock:^{ + [self sendImageDatas:self.pendingImages + withProviders:pendingImagesItemProviders + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; + }]; + + if (compressionPrompt) + { + [self.delegate shareExtensionManager:self showImageCompressionPrompt:compressionPrompt]; + } + } + else + { + dispatch_group_leave(requestsGroup); } } } @@ -268,6 +347,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeVideo]) { + dispatch_group_enter(requestsGroup); + [itemProvider loadItemForTypeIdentifier:UTTypeVideo options:nil completionHandler:^(NSURL *videoLocalUrl, NSError * _Null_unspecified error) { // Switch back on the main thread to handle correctly the UI change @@ -276,7 +357,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (weakSelf) { typeof(self) self = weakSelf; - [self sendVideo:videoLocalUrl toRoom:room extensionItem:item failureBlock:failureBlock]; + [self sendVideo:videoLocalUrl + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; } }); @@ -285,6 +370,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeMovie]) { + dispatch_group_enter(requestsGroup); + [itemProvider loadItemForTypeIdentifier:UTTypeMovie options:nil completionHandler:^(NSURL *videoLocalUrl, NSError * _Null_unspecified error) { // Switch back on the main thread to handle correctly the UI change @@ -293,7 +380,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (weakSelf) { typeof(self) self = weakSelf; - [self sendVideo:videoLocalUrl toRoom:room extensionItem:item failureBlock:failureBlock]; + [self sendVideo:videoLocalUrl + toRoom:room + successBlock:^{ + requestSuccess(item); + } failureBlock:requestFailure]; } }); @@ -302,6 +393,22 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } } } + + dispatch_group_notify(requestsGroup, dispatch_get_main_queue(), ^{ + [self resetPendingData]; + + if (firstRequestError) + { + if (failureBlock) + { + failureBlock(firstRequestError); + } + } + else + { + [self completeRequestReturningItems:returningExtensionItems completionHandler:nil]; + } + }); } - (BOOL)hasImageTypeContent @@ -364,15 +471,10 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) for (NSData *imageData in self.pendingImages) { - @autoreleasepool + if ([self isImageOrientationNotUpOrUndeterminedForImageData:imageData]) { - UIImage *image = [UIImage imageWithData:imageData]; - - if (image && image.imageOrientation != UIImageOrientationUp) - { - isAPendingImageNotOrientedUp = YES; - break; - } + isAPendingImageNotOrientedUp = YES; + break; } } @@ -555,6 +657,160 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) return YES; } +- (BOOL)areAllAttachmentsImages +{ + for (NSExtensionItem *item in self.shareExtensionContext.inputItems) + { + for (NSItemProvider *itemProvider in item.attachments) + { + if (![itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypeImage]) + { + return NO; + } + } + } + return YES; +} + +- (NSString*)utiFromImageTypeItemProvider:(NSItemProvider*)itemProvider +{ + NSString *uti; + + NSString *utiPNG = (__bridge NSString *)kUTTypePNG; + NSString *utiJPEG = (__bridge NSString *)kUTTypeJPEG; + + if ([itemProvider hasItemConformingToTypeIdentifier:utiPNG]) + { + uti = utiPNG; + } + else if ([itemProvider hasItemConformingToTypeIdentifier:utiJPEG]) + { + uti = utiJPEG; + } + else + { + uti = itemProvider.registeredTypeIdentifiers.firstObject; + } + + return uti; +} + +- (NSString*)utiFromImageData:(NSData*)imageData +{ + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); + NSString *uti = (NSString*)CGImageSourceGetType(imageSource); + CFRelease(imageSource); + return uti; +} + +- (NSString*)mimeTypeFromUTI:(NSString*)uti +{ + return (__bridge_transfer NSString *) UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti, kUTTagClassMIMEType); +} + +- (BOOL)isResizingSupportedForImageData:(NSData*)imageData +{ + NSString *imageUTI = [self utiFromImageData:imageData]; + return [self isResizingSupportedForUTI:imageUTI]; +} + +- (BOOL)isResizingSupportedForUTI:(NSString*)imageUTI +{ + if ([imageUTI isEqualToString:(__bridge NSString *)kUTTypePNG] || [imageUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG]) + { + return YES; + } + return NO; +} + +- (CGSize)imageSizeFromImageData:(NSData*)imageData +{ + CGFloat width = 0.0f; + CGFloat height = 0.0f; + + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); + + CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + + CFRelease(imageSource); + + if (imageProperties != NULL) + { + CFNumberRef widthNumber = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth); + CFNumberRef heightNumber = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight); + CFNumberRef orientationNumber = CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation); + + if (widthNumber != NULL) + { + CFNumberGetValue(widthNumber, kCFNumberCGFloatType, &width); + } + + if (heightNumber != NULL) + { + CFNumberGetValue(heightNumber, kCFNumberCGFloatType, &height); + } + + // Check orientation and flip size if required + if (orientationNumber != NULL) + { + int orientation; + CFNumberGetValue(orientationNumber, kCFNumberIntType, &orientation); + + // For orientation from kCGImagePropertyOrientationLeftMirrored to kCGImagePropertyOrientationLeft flip size + if (orientation >= 5) + { + CGFloat tempWidth = width; + width = height; + height = tempWidth; + } + } + + CFRelease(imageProperties); + } + + return CGSizeMake(width, height); +} + +- (NSNumber*)cgImageimageOrientationNumberFromImageData:(NSData*)imageData +{ + NSNumber *orientationNumber; + + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); + + CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + + CFRelease(imageSource); + + if (imageProperties != NULL) + { + CFNumberRef orientationNum = CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation); + + // Check orientation and flip size if required + if (orientationNum != NULL) + { + orientationNumber = (__bridge NSNumber *)orientationNum; + } + + CFRelease(imageProperties); + } + + return orientationNumber; +} + +- (BOOL)isImageOrientationNotUpOrUndeterminedForImageData:(NSData*)imageData +{ + BOOL isImageNotOrientedUp = YES; + + NSNumber *cgImageOrientationNumber = [self cgImageimageOrientationNumberFromImageData:imageData]; + + if (cgImageOrientationNumber && cgImageOrientationNumber.unsignedIntegerValue == (NSUInteger)kCGImagePropertyOrientationUp) + { + isImageNotOrientedUp = NO; + } + + return isImageNotOrientedUp; +} + #pragma mark - Notifications - (void)onMediaLoaderStateDidChange:(NSNotification *)notification @@ -586,7 +842,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) #pragma mark - Sharing -- (void)sendText:(NSString *)text toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock +- (void)sendText:(NSString *)text toRoom:(MXRoom *)room successBlock:(dispatch_block_t)successBlock failureBlock:(void(^)(NSError *error))failureBlock { [self didStartSendingToRoom:room]; if (!text) @@ -599,12 +855,10 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) return; } - __weak typeof(self) weakSelf = self; [room sendTextMessage:text success:^(NSString *eventId) { - if (weakSelf) + if (successBlock) { - typeof(self) self = weakSelf; - [self completeRequestReturningItems:@[extensionItem] completionHandler:nil]; + successBlock(); } } failure:^(NSError *error) { NSLog(@"[ShareExtensionManager] sendTextMessage failed."); @@ -615,7 +869,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) }]; } -- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock +- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room successBlock:(dispatch_block_t)successBlock failureBlock:(void(^)(NSError *error))failureBlock { [self didStartSendingToRoom:room]; if (!fileUrl) @@ -630,16 +884,13 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) NSString *mimeType; CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fileUrl pathExtension] , NULL); - mimeType = (__bridge_transfer NSString *) UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType); + mimeType = [self mimeTypeFromUTI:(__bridge NSString *)uti]; CFRelease(uti); - __weak typeof(self) weakSelf = self; - [room sendFile:fileUrl mimeType:mimeType localEcho:nil success:^(NSString *eventId) { - if (weakSelf) + if (successBlock) { - typeof(self) self = weakSelf; - [self completeRequestReturningItems:@[extensionItem] completionHandler:nil]; + successBlock(); } } failure:^(NSError *error) { NSLog(@"[ShareExtensionManager] sendFile failed."); @@ -650,10 +901,135 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } keepActualFilename:YES]; } - -- (void)sendImages:(NSMutableArray *)imageDatas withProviders:(NSArray*)itemProviders toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock +- (void)sendImageData:(NSData *)imageData withProvider:(NSItemProvider*)itemProvider toRoom:(MXRoom *)room successBlock:(dispatch_block_t)successBlock failureBlock:(void(^)(NSError *error))failureBlock { - if (imageDatas.count == 0) + [self didStartSendingToRoom:room]; + + NSString *imageUTI; + NSString *mimeType; + + // Try to get UTI plus mime type from NSItemProvider + imageUTI = [self utiFromImageTypeItemProvider:itemProvider]; + + if (imageUTI) + { + mimeType = [self mimeTypeFromUTI:imageUTI]; + } + + if (!mimeType) + { + // Try to get UTI plus mime type from image data + + imageUTI = [self utiFromImageData:imageData]; + + if (imageUTI) + { + mimeType = [self mimeTypeFromUTI:imageUTI]; + } + } + + // Sanity check + if (!mimeType) + { + NSLog(@"[ShareExtensionManager] sendImage failed. Cannot determine MIME type of %@", itemProvider); + if (failureBlock) + { + failureBlock(nil); + } + return; + } + + CGSize imageSize; + NSData *finalImageData; + + // Only resize JPEG or PNG files + if ([self isResizingSupportedForUTI:imageUTI]) + { + UIImage *convertedImage; + CGSize newImageSize; + + switch (self.imageCompressionMode) { + case ImageCompressionModeSmall: + newImageSize = CGSizeMake(MXKTOOLS_SMALL_IMAGE_SIZE, MXKTOOLS_SMALL_IMAGE_SIZE); + break; + case ImageCompressionModeMedium: + newImageSize = CGSizeMake(MXKTOOLS_MEDIUM_IMAGE_SIZE, MXKTOOLS_MEDIUM_IMAGE_SIZE); + break; + case ImageCompressionModeLarge: + newImageSize = CGSizeMake(self.actualLargeSize, self.actualLargeSize); + break; + default: + newImageSize = CGSizeZero; + break; + } + + if (CGSizeEqualToSize(newImageSize, CGSizeZero)) + { + // No resize to make + // Make sure the uploaded image orientation is up + if ([self isImageOrientationNotUpOrUndeterminedForImageData:imageData]) + { + UIImage *image = [UIImage imageWithData:imageData]; + convertedImage = [MXKTools forceImageOrientationUp:image]; + } + } + else + { + // Resize the image and set image in right orientation too + convertedImage = [MXKTools resizeImageWithData:imageData toFitInSize:newImageSize]; + } + + if (convertedImage) + { + if ([imageUTI isEqualToString:(__bridge NSString *)kUTTypePNG]) + { + finalImageData = UIImagePNGRepresentation(convertedImage); + } + else if ([imageUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG]) + { + finalImageData = UIImageJPEGRepresentation(convertedImage, 0.9); + } + + imageSize = convertedImage.size; + } + else + { + finalImageData = imageData; + imageSize = [self imageSizeFromImageData:imageData]; + } + } + else + { + finalImageData = imageData; + imageSize = [self imageSizeFromImageData:imageData]; + } + + UIImage *thumbnail = nil; + // Thumbnail is useful only in case of encrypted room + if (room.summary.isEncrypted) + { + thumbnail = [MXKTools resizeImageWithData:imageData toFitInSize:CGSizeMake(800, 600)]; + } + + [room sendImage:finalImageData withImageSize:imageSize mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) { + if (successBlock) + { + successBlock(); + } + } failure:^(NSError *error) { + + NSLog(@"[ShareExtensionManager] sendImage failed."); + if (failureBlock) + { + failureBlock(error); + } + + }]; +} + +- (void)sendImageDatas:(NSMutableArray *)imageDatas withProviders:(NSArray*)itemProviders toRoom:(MXRoom *)room successBlock:(dispatch_block_t)successBlock failureBlock:(void(^)(NSError *error))failureBlock +{ + if (imageDatas.count == 0 || imageDatas.count != itemProviders.count) { NSLog(@"[ShareExtensionManager] sendImages: no images to send."); @@ -666,130 +1042,55 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) [self didStartSendingToRoom:room]; - __block NSUInteger count = imageDatas.count; + dispatch_group_t requestsGroup = dispatch_group_create(); + __block NSError *firstRequestError; - for (NSInteger index = 0; index < imageDatas.count; index++) + NSUInteger index = 0; + + for (NSData *imageData in imageDatas) { @autoreleasepool { + dispatch_group_enter(requestsGroup); + NSItemProvider *itemProvider = itemProviders[index]; - NSData *imageData = imageDatas[index]; - UIImage *image = [UIImage imageWithData:imageData]; - if (!image) - { - NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed."); - if (failureBlock) - { - failureBlock(nil); - } - return; - } - - // Prepare the image - UIImage *convertedImage; - CGSize newImageSize; - - switch (self.imageCompressionMode) { - case ImageCompressionModeSmall: - newImageSize = CGSizeMake(MXKTOOLS_SMALL_IMAGE_SIZE, MXKTOOLS_SMALL_IMAGE_SIZE); - break; - case ImageCompressionModeMedium: - newImageSize = CGSizeMake(MXKTOOLS_MEDIUM_IMAGE_SIZE, MXKTOOLS_MEDIUM_IMAGE_SIZE); - break; - case ImageCompressionModeLarge: - newImageSize = CGSizeMake(self.actualLargeSize, self.actualLargeSize); - break; - default: - newImageSize = CGSizeZero; - break; - } - - if (CGSizeEqualToSize(newImageSize, CGSizeZero)) - { - // No resize to make - // Make sure the uploaded image orientation is up - convertedImage = [MXKTools forceImageOrientationUp:image]; - } - else - { - // Resize the image and set image in right orientation too - convertedImage = [MXKTools resizeImageWithData:imageData toFitInSize:newImageSize]; - } - - NSString *mimeType; - if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypePNG]) - { - mimeType = @"image/png"; - if (convertedImage != image) - { - imageData = UIImagePNGRepresentation(convertedImage); - } - } - else if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypeJPEG]) - { - mimeType = @"image/jpeg"; - if (convertedImage != image) - { - imageData = UIImageJPEGRepresentation(convertedImage, 0.9); - } - } - else - { - // Other image types like GIF - NSString *imageFileName = itemProvider.registeredTypeIdentifiers[0]; - mimeType = (__bridge_transfer NSString *) UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)imageFileName, kUTTagClassMIMEType); - } - - // Sanity check - if (!mimeType) - { - NSLog(@"[ShareExtensionManager] sendImage failed. Cannot determine MIME type of %@", itemProvider); - if (failureBlock) - { - failureBlock(nil); - } - return; - } - - UIImage *thumbnail = nil; - // Thumbnail is useful only in case of encrypted room - if (room.summary.isEncrypted) - { - thumbnail = [MXKTools reduceImage:convertedImage toFitInSize:CGSizeMake(800, 600)]; - if (thumbnail == convertedImage) - { - thumbnail = nil; - } - } - - __weak typeof(self) weakSelf = self; - - [room sendImage:imageData withImageSize:convertedImage.size mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) { + [self sendImageData:imageData withProvider:itemProvider toRoom:room successBlock:^{ + dispatch_group_leave(requestsGroup); + } failureBlock:^(NSError *error) { - if (!--count && weakSelf) + if (error && !firstRequestError) { - typeof(self) self = weakSelf; - - [self resetPendingData]; - [self completeRequestReturningItems:@[extensionItem] completionHandler:nil]; - } - - } failure:^(NSError *error) { - - NSLog(@"[ShareExtensionManager] sendImage failed."); - if (failureBlock) - { - failureBlock(error); + firstRequestError = error; } + dispatch_group_leave(requestsGroup); }]; - } + + index++; } + + dispatch_group_notify(requestsGroup, dispatch_get_main_queue(), ^{ + + if (firstRequestError) + { + if (failureBlock) + { + failureBlock(firstRequestError); + } + } + else + { + if (successBlock) + { + successBlock(); + } + } + }); } -- (void)sendVideo:(NSURL *)videoLocalUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock +- (void)sendVideo:(NSURL *)videoLocalUrl toRoom:(MXRoom *)room successBlock:(dispatch_block_t)successBlock failureBlock:(void(^)(NSError *error))failureBlock { [self didStartSendingToRoom:room]; if (!videoLocalUrl) @@ -812,13 +1113,10 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) UIImage *videoThumbnail = [[UIImage alloc] initWithCGImage:imageRef]; CFRelease(imageRef); - __weak typeof(self) weakSelf = self; - [room sendVideo:videoLocalUrl withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) { - if (weakSelf) + if (successBlock) { - typeof(self) self = weakSelf; - [self completeRequestReturningItems:@[extensionItem] completionHandler:nil]; + successBlock(); } } failure:^(NSError *error) { NSLog(@"[ShareExtensionManager] sendVideo failed."); @@ -837,7 +1135,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) - (void)setIsLoaded:(BOOL)isLoaded { - NSNumber *number = [NSNumber numberWithBool:isLoaded]; + NSNumber *number = @(isLoaded); objc_setAssociatedObject(self, @selector(isLoaded), number, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } diff --git a/RiotShareExtension/Modules/Fallback/FallbackViewController.m b/RiotShareExtension/Modules/Fallback/FallbackViewController.m index cae2cabce..42d213e82 100644 --- a/RiotShareExtension/Modules/Fallback/FallbackViewController.m +++ b/RiotShareExtension/Modules/Fallback/FallbackViewController.m @@ -15,7 +15,13 @@ */ #import "FallbackViewController.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" + +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif @interface FallbackViewController () @@ -29,7 +35,7 @@ { [super viewDidLoad]; - self.titleLabel.textColor = kRiotSecondaryTextColor; + self.titleLabel.textColor = ThemeService.shared.theme.textSecondaryColor; self.titleLabel.text = NSLocalizedStringFromTable(@"share_extension_auth_prompt", @"Vector", nil); } diff --git a/RiotShareExtension/Modules/Main/SharePresentingViewController.m b/RiotShareExtension/Modules/Main/SharePresentingViewController.m index 13c7390e8..ef57eba3c 100644 --- a/RiotShareExtension/Modules/Main/SharePresentingViewController.m +++ b/RiotShareExtension/Modules/Main/SharePresentingViewController.m @@ -17,6 +17,13 @@ #import "SharePresentingViewController.h" #import "ShareViewController.h" #import "ShareExtensionManager.h" +#import "ThemeService.h" + +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif @interface SharePresentingViewController () @@ -35,6 +42,9 @@ sharedManager.primaryViewController = self; sharedManager.shareExtensionContext = self.extensionContext; + // Set up current theme + ThemeService.shared.themeId = RiotSettings.shared.userInterfaceTheme; + [self presentShareViewController]; } diff --git a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m index 9c3f126d7..2242cb599 100644 --- a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m +++ b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m @@ -19,9 +19,15 @@ #import "NSBundle+MatrixKit.h" #import "ShareExtensionManager.h" #import "RecentCellData.h" -#import "RiotDesignValues.h" +#import "ThemeService.h" #import +#ifdef IS_SHARE_EXTENSION +#import "RiotShareExtension-Swift.h" +#else +#import "Riot-Swift.h" +#endif + @interface RoomsListViewController () @property (nonatomic) MXKPieChartHUD *hudView; @@ -90,7 +96,7 @@ self.recentsSearchBar.searchBarStyle = UISearchBarStyleMinimal; self.recentsSearchBar.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil); - self.recentsSearchBar.tintColor = kRiotColorGreen; + self.recentsSearchBar.tintColor = ThemeService.shared.theme.tintColor; _tableSearchBar.tintColor = self.recentsSearchBar.tintColor; } diff --git a/RiotShareExtension/Modules/Share/ShareViewController.m b/RiotShareExtension/Modules/Share/ShareViewController.m index 6f6b785f7..5d8aa81fd 100644 --- a/RiotShareExtension/Modules/Share/ShareViewController.m +++ b/RiotShareExtension/Modules/Share/ShareViewController.m @@ -102,7 +102,7 @@ else { NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary; - NSString *bundleDisplayName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; + NSString *bundleDisplayName = infoDictionary[@"CFBundleDisplayName"]; self.tittleLabel.text = bundleDisplayName; [self configureFallbackViewController]; } diff --git a/RiotShareExtension/SupportingFiles/Info.plist b/RiotShareExtension/SupportingFiles/Info.plist index c96f21578..50340428b 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -17,15 +17,17 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.7.11 + 0.8.0 CFBundleVersion - 0.7.11 + 0.8.0 NSExtension NSExtensionAttributes NSExtensionActivationRule + NSExtensionActivationDictionaryVersion + 2 NSExtensionActivationSupportsImageWithMaxCount 5 NSExtensionActivationSupportsMovieWithMaxCount diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index 6446b4502..7c7581001 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.7.11 + 0.8.0 CFBundleVersion - 0.7.11 + 0.8.0 NSExtension NSExtensionAttributes diff --git a/Tools/SwiftGen/Templates/Strings/flat-swift4-vector.stencil b/Tools/SwiftGen/Templates/Strings/flat-swift4-vector.stencil new file mode 100644 index 000000000..a7f61cdee --- /dev/null +++ b/Tools/SwiftGen/Templates/Strings/flat-swift4-vector.stencil @@ -0,0 +1,84 @@ +// swiftlint:disable all +// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - Strings + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + _ p{{forloop.counter}}: {{type}}{{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "UnsafeRawPointer" %} + Int(bitPattern: p{{forloop.counter}}) + {% else %} + p{{forloop.counter}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro recursiveBlock table item %} + {% for string in item.strings %} + {% if not param.noComments %} + /// {{string.translation}} + {% endif %} + {% if string.types %} + {{accessModifier}} static func {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { + return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %}) + } + {% else %} + {{accessModifier}} static var {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { + return {{enumName}}.tr("{{table}}", "{{string.key}}") + } + {% endif %} + {% endfor %} + {% for child in item.children %} + {% call recursiveBlock table child %} + {% endfor %} +{% endmacro %} +// swiftlint:disable function_parameter_count identifier_name line_length type_body_length +{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %} +{{accessModifier}} enum {{enumName}} { + {% if tables.count > 1 %} + {% for table in tables %} + {{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call recursiveBlock tables.first.name tables.first.levels %} + {% endif %} +} +// swiftlint:enable function_parameter_count identifier_name line_length type_body_length + +// MARK: - Implementation Details + +extension {{enumName}} { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + let format = NSLocalizedString(key, tableName: table, bundle: Bundle(for: BundleToken.self), comment: "") + let locale: Locale + + if let localeIdentifier = Bundle.mxk_language() { + locale = Locale(identifier: localeIdentifier) + } else if let fallbackLocaleIdentifier = Bundle.mxk_fallbackLanguage() { + locale = Locale(identifier: fallbackLocaleIdentifier) + } else { + locale = Locale.current + } + + return String(format: format, locale: locale, arguments: args) + } +} + +private final class BundleToken {} +{% else %} +// No string found +{% endif %} \ No newline at end of file diff --git a/Tools/SwiftGen/swiftgen-config.yml b/Tools/SwiftGen/swiftgen-config.yml new file mode 100755 index 000000000..7da2a05f6 --- /dev/null +++ b/Tools/SwiftGen/swiftgen-config.yml @@ -0,0 +1,29 @@ +input_dir: ../../Riot/ +output_dir: ../../Riot/Generated/ +ib: + - inputs: + - Modules/KeyBackup/ + outputs: + - templateName: scenes-swift4 + output: Storyboards.swift +strings: + inputs: Assets/en.lproj/Vector.strings + outputs: + - templatePath: Templates/Strings/flat-swift4-vector.stencil + output: Strings.swift + params: + enumName: VectorL10n +xcassets: + - inputs: + - Assets/Images.xcassets + - Assets/SharedImages.xcassets + outputs: + - templateName: swift4 + output: Images.swift +plist: + inputs: Assets/Riot-Defaults.plist + outputs: + templateName: runtime-swift4 + output: RiotDefaults.swift + params: + enumName: RiotDefaults \ No newline at end of file