mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-12 02:45:52 +02:00
Finish v0.8.5
This commit is contained in:
+18
-1
@@ -1,9 +1,26 @@
|
||||
Changes in 0.8.5 (2019-xx-xx)
|
||||
Changes in 0.8.5 (2019-05-03)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* Upgrade MatrixKit version ([v0.9.9](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.9.9)).
|
||||
* Push: Add more logs to track spontaneously disabling (#2348).
|
||||
* Widgets: Use scalar prod urls in Riot mobile apps (#2349).
|
||||
* Productiviy: Create templates (see Tools/Templates/README.md).
|
||||
* Notifications: Use UserNotifications framework for local notifications (iOS 10+), thanks to @fridtjof (PR #2207).
|
||||
* Notifications: Added titles to notifications on iOS 10+, thanks to @fridtjof (PR #2347).
|
||||
* iOS 12 Notification: Group them by room (#2337 and PR #2347 thanks to @fridtjof).
|
||||
* Notifications: When navigate to a room, remove associated delivered notifications (#2337).
|
||||
* Key backup: Adjust wording for untrusted backup to match Riot Web.
|
||||
* Jitsi integration: Use the matching WebRTC framework (#1483).
|
||||
* Fastlane: Set iCloud container environment (PR #2385).
|
||||
* Remove code used for iOS 9 only (PR #2386).
|
||||
|
||||
Bug fix:
|
||||
* Share extension: Fix a crash when receive a memory warning (PR #2352).
|
||||
* Upgraded rooms show up in the share extension twice (#2293).
|
||||
* +N read receipt text is invisible on dark theme (#2294).
|
||||
* Avoid crashes with tableview reload animation in settings and room settings (PR #2364).
|
||||
* Media picker: Fix some retain cycles (PR #2382).
|
||||
|
||||
Changes in 0.8.4 (2019-03-21)
|
||||
===============================================
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, "9.0"
|
||||
platform :ios, '10.0'
|
||||
|
||||
# Use frameforks to allow usage of pod written in Swift (like PiwikTracker)
|
||||
use_frameworks!
|
||||
@@ -7,7 +7,7 @@ use_frameworks!
|
||||
|
||||
# Different flavours of pods to MatrixKit
|
||||
# The current MatrixKit pod version
|
||||
$matrixKitVersion = '0.9.8'
|
||||
$matrixKitVersion = '0.9.9'
|
||||
|
||||
# The develop branch version
|
||||
#$matrixKitVersion = 'develop'
|
||||
@@ -43,18 +43,15 @@ def import_MatrixKitAppExtension
|
||||
if $matrixKitVersion == 'local'
|
||||
pod 'MatrixSDK', :path => '../matrix-ios-sdk/MatrixSDK.podspec'
|
||||
pod 'MatrixSDK/SwiftSupport', :path => '../matrix-ios-sdk/MatrixSDK.podspec'
|
||||
pod 'MatrixSDK/JingleCallStack', :path => '../matrix-ios-sdk/MatrixSDK.podspec'
|
||||
pod 'MatrixKit/AppExtension', :path => '../matrix-ios-kit/MatrixKit.podspec'
|
||||
else
|
||||
if $matrixKitVersion == 'develop'
|
||||
pod 'MatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
|
||||
pod 'MatrixSDK/SwiftSupport', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
|
||||
pod 'MatrixSDK/JingleCallStack', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop'
|
||||
pod 'MatrixKit/AppExtension', :git => 'https://github.com/matrix-org/matrix-ios-kit.git', :branch => 'develop'
|
||||
else
|
||||
pod 'MatrixKit/AppExtension', $matrixKitVersion
|
||||
pod 'MatrixSDK/SwiftSupport'
|
||||
pod 'MatrixSDK/JingleCallStack'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+28
-28
@@ -42,46 +42,47 @@ PODS:
|
||||
- GBDeviceInfo/Core (5.2.0)
|
||||
- GZIP (1.2.2)
|
||||
- HPGrowingTextView (1.1)
|
||||
- JitsiMeetSDK (2.1.0)
|
||||
- libbase58 (0.1.4)
|
||||
- libPhoneNumber-iOS (0.9.13)
|
||||
- MatrixKit (0.9.8):
|
||||
- MatrixKit (0.9.9):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.9.8)
|
||||
- MatrixSDK (= 0.12.4)
|
||||
- MatrixKit/AppExtension (0.9.8):
|
||||
- MatrixKit/Core (= 0.9.9)
|
||||
- MatrixSDK (= 0.12.5)
|
||||
- MatrixKit/AppExtension (0.9.9):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- DTCoreText/Extension
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.12.4)
|
||||
- MatrixKit/Core (0.9.8):
|
||||
- MatrixSDK (= 0.12.5)
|
||||
- MatrixKit/Core (0.9.9):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.12.4)
|
||||
- MatrixSDK (0.12.4):
|
||||
- MatrixSDK/Core (= 0.12.4)
|
||||
- MatrixSDK/Core (0.12.4):
|
||||
- MatrixSDK (= 0.12.5)
|
||||
- MatrixSDK (0.12.5):
|
||||
- MatrixSDK/Core (= 0.12.5)
|
||||
- MatrixSDK/Core (0.12.5):
|
||||
- AFNetworking (~> 3.2.0)
|
||||
- GZIP (~> 1.2.2)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.0.0)
|
||||
- OLMKit (~> 3.1.0)
|
||||
- Realm (~> 3.13.1)
|
||||
- MatrixSDK/JingleCallStack (0.12.4):
|
||||
- MatrixSDK/JingleCallStack (0.12.5):
|
||||
- JitsiMeetSDK (~> 2.1.0)
|
||||
- MatrixSDK/Core
|
||||
- WebRTC (= 63.11.20455)
|
||||
- MatrixSDK/SwiftSupport (0.12.4):
|
||||
- MatrixSDK/SwiftSupport (0.12.5):
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.0.0):
|
||||
- OLMKit/olmc (= 3.0.0)
|
||||
- OLMKit/olmcpp (= 3.0.0)
|
||||
- OLMKit/olmc (3.0.0)
|
||||
- OLMKit/olmcpp (3.0.0)
|
||||
- OLMKit (3.1.0):
|
||||
- OLMKit/olmc (= 3.1.0)
|
||||
- OLMKit/olmcpp (= 3.1.0)
|
||||
- OLMKit/olmc (3.1.0)
|
||||
- OLMKit/olmcpp (3.1.0)
|
||||
- PiwikTracker (4.4.2):
|
||||
- PiwikTracker/Core (= 4.4.2)
|
||||
- PiwikTracker/Core (4.4.2)
|
||||
@@ -95,15 +96,14 @@ PODS:
|
||||
- Reusable/View (4.0.5)
|
||||
- SwiftGen (6.1.0)
|
||||
- SwiftLint (0.30.1)
|
||||
- WebRTC (63.11.20455)
|
||||
- zxcvbn-ios (1.0.4)
|
||||
|
||||
DEPENDENCIES:
|
||||
- cmark
|
||||
- DTCoreText
|
||||
- GBDeviceInfo (~> 5.2.0)
|
||||
- MatrixKit (= 0.9.8)
|
||||
- MatrixKit/AppExtension (= 0.9.8)
|
||||
- MatrixKit (= 0.9.9)
|
||||
- MatrixKit/AppExtension (= 0.9.9)
|
||||
- MatrixSDK/JingleCallStack
|
||||
- MatrixSDK/SwiftSupport
|
||||
- OLMKit
|
||||
@@ -122,6 +122,7 @@ SPEC REPOS:
|
||||
- GBDeviceInfo
|
||||
- GZIP
|
||||
- HPGrowingTextView
|
||||
- JitsiMeetSDK
|
||||
- libbase58
|
||||
- libPhoneNumber-iOS
|
||||
- MatrixKit
|
||||
@@ -131,7 +132,6 @@ SPEC REPOS:
|
||||
- Reusable
|
||||
- SwiftGen
|
||||
- SwiftLint
|
||||
- WebRTC
|
||||
- zxcvbn-ios
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -152,19 +152,19 @@ SPEC CHECKSUMS:
|
||||
GBDeviceInfo: 2c65ceb9404f9079264d4c238f5b81916fdfc5e2
|
||||
GZIP: 12374d285e3b5d46cfcd480700fcfc7e16caf4f1
|
||||
HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19
|
||||
JitsiMeetSDK: 3e66564af7f38a19142338955dd7f581801852b3
|
||||
libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd
|
||||
libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa
|
||||
MatrixKit: 098ddd270d1dec86a85ea93a85a00e37b2d80c11
|
||||
MatrixSDK: 310efb69f70b4d7772f6f134b06fbb9ec238e6a7
|
||||
OLMKit: 88eda69110489f817d59bcb4353b7c247570aa4f
|
||||
MatrixKit: 6f553797e1ad42794b5336afb5cecb975ec69daa
|
||||
MatrixSDK: ed0d0cee4877955052f19730bb3ee727e01ec948
|
||||
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
|
||||
PiwikTracker: 42862c7b13028065c3dfd36b4dc38db8a5765acf
|
||||
Realm: 50071da38fe079e0735e47c9f2eae738c68c5996
|
||||
Reusable: 188be1a54ac0691bc66e5bb24ec6eb91971b315b
|
||||
SwiftGen: f872ca75cbd17bf7103c17f13dcfa0d9a15667b0
|
||||
SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7
|
||||
WebRTC: f2a6203584745fe53532633397557876b5d71640
|
||||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
|
||||
PODFILE CHECKSUM: 63fb661f957b3fa4c00765bc4d28bed41892847c
|
||||
PODFILE CHECKSUM: cfb6be050dfbb227d58b14434629e447ea54554b
|
||||
|
||||
COCOAPODS: 1.6.1
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ CocoaPods command::
|
||||
$ pod install
|
||||
|
||||
This will load all dependencies for the Riot source code, including MatrixKit
|
||||
and MatrixSDK. You will need an recent and updated (``pod update``) install of
|
||||
and MatrixSDK. You will need an recent and updated (``pod setup``) install of
|
||||
CocoaPods.
|
||||
|
||||
Then, open ``Riot.xcworkspace`` with Xcode
|
||||
|
||||
+480
-132
File diff suppressed because it is too large
Load Diff
+8
-2
@@ -17,6 +17,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
#import "MasterTabBarController.h"
|
||||
#import "JitsiViewController.h"
|
||||
@@ -37,7 +38,7 @@ extern NSString *const kAppDelegateDidTapStatusBarNotification;
|
||||
*/
|
||||
extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, MXKCallViewControllerDelegate, UISplitViewControllerDelegate, UINavigationControllerDelegate, JitsiViewControllerDelegate>
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, MXKCallViewControllerDelegate, UISplitViewControllerDelegate, UINavigationControllerDelegate, JitsiViewControllerDelegate, UNUserNotificationCenterDelegate>
|
||||
{
|
||||
BOOL isPushRegistered;
|
||||
|
||||
@@ -119,7 +120,7 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
|
||||
Log out all the accounts without confirmation.
|
||||
Show the authentication screen on successful logout.
|
||||
|
||||
@param sendLogoutRequest Indicate whether send logout request to home server.
|
||||
@param sendLogoutRequest Indicate whether send logout request to homeserver.
|
||||
@param completion the block to execute at the end of the operation.
|
||||
*/
|
||||
- (void)logoutSendingRequestServer:(BOOL)sendLogoutServerRequest
|
||||
@@ -143,6 +144,11 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
|
||||
|
||||
#pragma mark - Matrix Room handling
|
||||
|
||||
// Show a room and jump to the given event if event id is not nil otherwise go to last messages.
|
||||
- (void)showRoom:(NSString*)roomId andEventId:(NSString*)eventId withMatrixSession:(MXSession*)mxSession restoreInitialDisplay:(BOOL)restoreInitialDisplay completion:(void (^)(void))completion;
|
||||
|
||||
- (void)showRoom:(NSString*)roomId andEventId:(NSString*)eventId withMatrixSession:(MXSession*)mxSession restoreInitialDisplay:(BOOL)restoreInitialDisplay;
|
||||
|
||||
- (void)showRoom:(NSString*)roomId andEventId:(NSString*)eventId withMatrixSession:(MXSession*)mxSession;
|
||||
|
||||
// Creates a new direct chat with the provided user id
|
||||
|
||||
+649
-213
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,11 @@
|
||||
<key>matrixApps</key>
|
||||
<true/>
|
||||
<key>integrationsUiUrl</key>
|
||||
<string>https://scalar-staging.riot.im/scalar-web/</string>
|
||||
<string>https://scalar.vector.im/</string>
|
||||
<key>integrationsRestUrl</key>
|
||||
<string>https://scalar-staging.riot.im/scalar/api</string>
|
||||
<string>https://scalar.vector.im/api</string>
|
||||
<key>jitsiServerURL</key>
|
||||
<string>https://jitsi.riot.im</string>
|
||||
<key>integrationsWidgetsUrls</key>
|
||||
<array>
|
||||
<string>https://scalar-staging.riot.im/scalar/api</string>
|
||||
|
||||
@@ -282,11 +282,11 @@
|
||||
"auth_add_email_and_phone_message" = "Füge eine E-Mail-Adresse und eine Telefonnummer hinzu, damit dich andere Benutzer finden können. Über die E-Mail-Adresse kannst du das Passwort zurücksetzen.";
|
||||
"auth_use_server_options" = "Individuelle Server Optionen";
|
||||
"auth_email_validation_message" = "Prüfe dein E-Mail-Konto um mit der Registrierung fortzufahren";
|
||||
"auth_recaptcha_message" = "Dieser Home-Server will sicherstellen dass du kein Robot bist";
|
||||
"auth_recaptcha_message" = "Dieser Homeserver will sicherstellen dass du kein Robot bist";
|
||||
"auth_reset_password_message" = "E-Mail-Adresse angeben, um das Passwort zurückzusetzen:";
|
||||
"auth_reset_password_missing_email" = "Die E-Mail-Adresse die mit dem Konto verbunden ist muss eingegeben werden.";
|
||||
"auth_reset_password_error_unauthorized" = "Konnte E-Mail-Adresse nicht verifizieren. Klicke den Link in der Registrierungs-E-Mail";
|
||||
"auth_reset_password_error_not_found" = "Diese E-Mail-Adresse ist nicht mit einer Matrix-ID auf diesem Home-Server verknüpft.";
|
||||
"auth_reset_password_error_not_found" = "Diese E-Mail-Adresse ist nicht mit einer Matrix-ID auf diesem Homeserver verknüpft.";
|
||||
"auth_reset_password_success_message" = "Dein Passwort wurde zurückgesetzt.\n\nDu wurdest von allen Geräten abgemeldet, diese bekommen jetzt keine Push-Benachrichtigungen mehr. Um diese wiedereinzuschalten, melde dich auf den Geräten erneut an.";
|
||||
"auth_add_email_and_phone_warning" = "Registrierung mit E-Mail und Telefonnummer zugleich ist noch nicht unterstützt. Nur die Telefonnummer wird berücksichtigt. Du kannst deine E-Mail-Adresse in deinem Profil ergänzen.";
|
||||
"room_creation_make_public_prompt_msg" = "Sicher, dass du diesen Raum öffentlich machen willst? Jeder kann deine Nachrichten lesen und dem Raum beitreten.";
|
||||
@@ -368,7 +368,7 @@
|
||||
"directory_server_picker_title" = "Wähle ein Verzeichnis";
|
||||
"directory_server_all_rooms" = "Alle Räume auf %@ Server";
|
||||
"directory_server_all_native_rooms" = "Alle nativen Matrix-Räume";
|
||||
"directory_server_type_homeserver" = "Gib einen Home-Server ein, um davon öffentliche Räume zu listen";
|
||||
"directory_server_type_homeserver" = "Gib einen Homeserver ein, um davon öffentliche Räume zu listen";
|
||||
"directory_server_placeholder" = "matrix.org";
|
||||
// Others
|
||||
"or" = "oder";
|
||||
|
||||
@@ -19,4 +19,5 @@
|
||||
"NSPhotoLibraryUsageDescription" = "The photo library is used to send photos and videos.";
|
||||
"NSMicrophoneUsageDescription" = "The microphone is used to take videos, make calls.";
|
||||
"NSContactsUsageDescription" = "In order to show you which of your contacts are already using Riot or Matrix, we can send the email addresses and phone numbers in your address book to your Matrix Identity Server. New Vector does not store this data or use it for any other purpose. For more information please see the privacy policy page in application settings.";
|
||||
"NSCalendarsUsageDescription" = "See your scheduled meetings in the app.";
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/* Message title for a specific person in a named room */
|
||||
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@";
|
||||
|
||||
/** Single, end-to-end encrypted messages (ie. we don't know what they say) */
|
||||
|
||||
/* New message from a specific person, not referencing a room */
|
||||
"MSG_FROM_USER" = "Message from %@";
|
||||
"MSG_FROM_USER" = "%@ sent a message";
|
||||
|
||||
/* New message from a specific person in a named room */
|
||||
"MSG_FROM_USER_IN_ROOM" = "%@ posted in %@";
|
||||
@@ -39,7 +42,7 @@
|
||||
/** Image Messages **/
|
||||
|
||||
/* New action message from a specific person, not referencing a room. */
|
||||
"IMAGE_FROM_USER" = "%@ sent you a picture %@";
|
||||
"IMAGE_FROM_USER" = "%@ sent a picture %@";
|
||||
|
||||
/* New action message from a specific person in a named room. */
|
||||
"IMAGE_FROM_USER_IN_ROOM" = "%@ posted a picture %@ in %@";
|
||||
@@ -50,6 +53,9 @@
|
||||
/* A single unread message */
|
||||
"SINGLE_UNREAD" = "You received a message";
|
||||
|
||||
/* Sticker from a specific person, not referencing a room. */
|
||||
"STICKER_FROM_USER" = "%@ sent a sticker";
|
||||
|
||||
/** Coalesced messages **/
|
||||
|
||||
/* Multiple unread messages in a room */
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"auth_invalid_email" = "This doesn't look like a valid email address";
|
||||
"auth_invalid_phone" = "This doesn't look like a valid phone number";
|
||||
"auth_missing_password" = "Missing password";
|
||||
"auth_add_email_message" = "Add an email address to your account to let users discover you, and let you reset password.";
|
||||
"auth_add_email_message" = "Add an email address to your account to let users discover you, and to reset your password.";
|
||||
"auth_add_phone_message" = "Add a phone number to your account to let users discover you.";
|
||||
"auth_add_email_phone_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.";
|
||||
"auth_add_email_and_phone_message" = "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.";
|
||||
@@ -102,14 +102,14 @@
|
||||
"auth_msisdn_validation_title" = "Verification Pending";
|
||||
"auth_msisdn_validation_message" = "We\'ve sent an SMS with an activation code. Please enter this code below.";
|
||||
"auth_msisdn_validation_error" = "Unable to verify phone number.";
|
||||
"auth_recaptcha_message" = "This Home Server would like to make sure you are not a robot";
|
||||
"auth_recaptcha_message" = "This homeserver would like to make sure you are not a robot";
|
||||
"auth_reset_password_message" = "To reset your password, enter the email address linked to your account:";
|
||||
"auth_reset_password_missing_email" = "The email address linked to your account must be entered.";
|
||||
"auth_reset_password_missing_password" = "A new password must be entered.";
|
||||
"auth_reset_password_email_validation_message" = "An email has been sent to %@. Once you've followed the link it contains, click below.";
|
||||
"auth_reset_password_next_step_button" = "I have verified my email address";
|
||||
"auth_reset_password_error_unauthorized" = "Failed to verify email address: make sure you clicked the link in the email";
|
||||
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
|
||||
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this homeserver.";
|
||||
"auth_reset_password_success_message" = "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.";
|
||||
"auth_add_email_and_phone_warning" = "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.";
|
||||
"auth_accept_policies" = "Please review and accept the policies of this homeserver:";
|
||||
@@ -326,7 +326,7 @@
|
||||
"settings_mark_all_as_read" = "Mark all messages as read";
|
||||
"settings_report_bug" = "Report bug";
|
||||
"settings_clear_cache" = "Clear cache";
|
||||
"settings_config_home_server" = "Home server is %@";
|
||||
"settings_config_home_server" = "Homeserver is %@";
|
||||
"settings_config_identity_server" = "Identity server is %@";
|
||||
"settings_config_user_id" = "Logged in as %@";
|
||||
|
||||
@@ -431,11 +431,11 @@
|
||||
"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_signout_warning" = "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.";
|
||||
"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_not_valid" = "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.";
|
||||
"settings_key_backup_info_progress" = "Backing up %@ keys...";
|
||||
"settings_key_backup_info_progress_done" = "All keys backed up";
|
||||
|
||||
@@ -449,7 +449,7 @@
|
||||
"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_button_connect" = "Connect this device to 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.";
|
||||
|
||||
@@ -694,7 +694,7 @@
|
||||
"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_setup_connect_action_with_existing_backup" = "Connect this device to Key Backup";
|
||||
"key_backup_setup_intro_manual_export_info" = "(Advanced)";
|
||||
"key_backup_setup_intro_manual_export_action" = "Manually export keys";
|
||||
|
||||
@@ -773,7 +773,7 @@
|
||||
// Recover
|
||||
|
||||
"key_backup_recover_banner_title" = "Never lose encrypted messages";
|
||||
"key_backup_recover_banner_subtitle" = "Use Key Backup";
|
||||
"key_backup_recover_connent_banner_subtitle" = "Connect this device to Key Backup";
|
||||
|
||||
// MARK: Sign out warning
|
||||
|
||||
@@ -792,3 +792,99 @@
|
||||
"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";
|
||||
|
||||
// MARK: - Device Verification
|
||||
"device_verification_title" = "Verify device";
|
||||
"device_verification_security_advice" = "For maximum security, we recommend you do this in person or use another trusted means of communication";
|
||||
"device_verification_cancelled" = "The other party cancelled the verification.";
|
||||
"device_verification_cancelled_by_me" = "The verification has been cancelled. Reason: %@";
|
||||
"device_verification_error_cannot_load_device" = "Cannot load device information.";
|
||||
|
||||
// Mark: Incoming
|
||||
"device_verification_incoming_title" = "Incoming Verification Request";
|
||||
"device_verification_incoming_description_1" = "Verify this device to mark it as trusted. Trusting devices of partners gives you extra peace of mind when using end-to-end encrypted messages.";
|
||||
"device_verification_incoming_description_2" = "Verifying this device will mark it as trusted, and also mark your device as trusted to the partner.";
|
||||
|
||||
// MARK: Start
|
||||
"device_verification_start_title" = "Verify by comparing a short text string";
|
||||
"device_verification_start_wait_partner" = "Waiting for partner to accept...";
|
||||
"device_verification_start_use_legacy" = "Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification.";
|
||||
"device_verification_start_verify_button" = "Begin Verifying";
|
||||
"device_verification_start_use_legacy_action" = "Use Legacy Verification";
|
||||
|
||||
// MARK: Verify
|
||||
"device_verification_verify_title_emoji" = "Verify this device by confirming the following emoji appear on the screen of the partner";
|
||||
"device_verification_verify_title_number" = "Verify this device by confirming the following numbers appear on the screen of the partner";
|
||||
"device_verification_verify_wait_partner" = "Waiting for partner to confirm...";
|
||||
|
||||
// MARK: Verified
|
||||
"device_verification_verified_title" = "Verified!";
|
||||
"device_verification_verified_description_1" = "You've successfully verified this device.";
|
||||
"device_verification_verified_description_2" = "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.";
|
||||
"device_verification_verified_got_it_button" = "Got it";
|
||||
|
||||
// MARK: Emoji
|
||||
"device_verification_emoji_dog" = "Dog";
|
||||
"device_verification_emoji_cat" = "Cat";
|
||||
"device_verification_emoji_lion" = "Lion";
|
||||
"device_verification_emoji_horse" = "Horse";
|
||||
"device_verification_emoji_unicorn" = "Unicorn";
|
||||
"device_verification_emoji_pig" = "Pig";
|
||||
"device_verification_emoji_elephant" = "Elephant";
|
||||
"device_verification_emoji_rabbit" = "Rabbit";
|
||||
"device_verification_emoji_panda" = "Panda";
|
||||
"device_verification_emoji_rooster" = "Rooster";
|
||||
"device_verification_emoji_penguin" = "Penguin";
|
||||
"device_verification_emoji_turtle" = "Turtle";
|
||||
"device_verification_emoji_fish" = "Fish";
|
||||
"device_verification_emoji_octopus" = "Octopus";
|
||||
"device_verification_emoji_butterfly" = "Butterfly";
|
||||
"device_verification_emoji_flower" = "Flower";
|
||||
"device_verification_emoji_tree" = "Tree";
|
||||
"device_verification_emoji_cactus" = "Cactus";
|
||||
"device_verification_emoji_mushroom" = "Mushroom";
|
||||
"device_verification_emoji_globe" = "Globe";
|
||||
"device_verification_emoji_moon" = "Moon";
|
||||
"device_verification_emoji_cloud" = "Cloud";
|
||||
"device_verification_emoji_fire" = "Fire";
|
||||
"device_verification_emoji_banana" = "Banana";
|
||||
"device_verification_emoji_apple" = "Apple";
|
||||
"device_verification_emoji_strawberry" = "Strawberry";
|
||||
"device_verification_emoji_corn" = "Corn";
|
||||
"device_verification_emoji_pizza" = "Pizza";
|
||||
"device_verification_emoji_cake" = "Cake";
|
||||
"device_verification_emoji_heart" = "Heart";
|
||||
"device_verification_emoji_smiley" = "Smiley";
|
||||
"device_verification_emoji_robot" = "Robot";
|
||||
"device_verification_emoji_hat" = "Hat";
|
||||
"device_verification_emoji_glasses" = "Glasses";
|
||||
"device_verification_emoji_spanner" = "Spanner";
|
||||
"device_verification_emoji_santa" = "Santa";
|
||||
"device_verification_emoji_thumbs up" = "Thumbs up";
|
||||
"device_verification_emoji_umbrella" = "Umbrella";
|
||||
"device_verification_emoji_hourglass" = "Hourglass";
|
||||
"device_verification_emoji_clock" = "Class";
|
||||
"device_verification_emoji_gift" = "Gift";
|
||||
"device_verification_emoji_light bulb" = "Light Bulb";
|
||||
"device_verification_emoji_book" = "Book";
|
||||
"device_verification_emoji_pencil" = "Pencil";
|
||||
"device_verification_emoji_paperclip" = "Paperclip";
|
||||
"device_verification_emoji_scissors" = "Scissors";
|
||||
"device_verification_emoji_padlock" = "Padlock";
|
||||
"device_verification_emoji_key" = "Key";
|
||||
"device_verification_emoji_hammer" = "Hammer";
|
||||
"device_verification_emoji_telephone" = "Telephone";
|
||||
"device_verification_emoji_flag" = "Flag";
|
||||
"device_verification_emoji_train" = "Train";
|
||||
"device_verification_emoji_bicycle" = "Bicycle";
|
||||
"device_verification_emoji_aeroplane" = "Aeroplane";
|
||||
"device_verification_emoji_rocket" = "Rocket";
|
||||
"device_verification_emoji_trophy" = "Trophy";
|
||||
"device_verification_emoji_ball" = "Ball";
|
||||
"device_verification_emoji_guitar" = "Guitar";
|
||||
"device_verification_emoji_trumpet" = "Trumpet";
|
||||
"device_verification_emoji_bell" = "Ball";
|
||||
"device_verification_emoji_anchor" = "Anchor";
|
||||
"device_verification_emoji_headphones" = "Headphones";
|
||||
"device_verification_emoji_folder" = "Folder";
|
||||
"device_verification_emoji_pin" = "Pin";
|
||||
|
||||
@@ -47,6 +47,6 @@
|
||||
/* Incoming named video conference invite from a specific person */
|
||||
"VIDEO_CONF_NAMED_FROM_USER" = "映像つき会議通話の着信 from %@: '%@'";
|
||||
/* A single unread message in a room */
|
||||
"SINGLE_UNREAD_IN_ROOM" = "%@にメッセージを受け取りました";
|
||||
"SINGLE_UNREAD_IN_ROOM" = "%@にメッセージを受け取りました";
|
||||
/* A single unread message */
|
||||
"SINGLE_UNREAD" = "あなたはメッセージを受け取りました";
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
// Room Preview
|
||||
"room_preview_invitation_format" = "あなたは %@ さんに呼ばれてこの部屋へ参加しました";
|
||||
"room_preview_subtitle" = "これは部屋の下見です。発言があっても部屋は更新されません。";
|
||||
"room_preview_unlinked_email_warning" = "このアカウントに関連付けられていない %@ 宛に招待が送信されました。別のアカウントでログインするか、この電子メールアドレスをこのアカウントに追加することができます。";
|
||||
"room_preview_unlinked_email_warning" = "このアカウントに関連付けられていない %@ 宛に招待が送信されました。別のアカウントでログインするか、この電子メールアドレスをこのアカウントに追加することができます。";
|
||||
"room_preview_try_join_an_unknown_room" = "あなたは %@ へ接続しようとしています。この会議に参加しますか?";
|
||||
"room_preview_try_join_an_unknown_room_default" = "部屋";
|
||||
// Settings
|
||||
@@ -435,7 +435,7 @@
|
||||
"no_voip_title" = "通話着信中";
|
||||
"no_voip" = "%@ さんから通話の着信がありましたが、 %@ は通話をまだサポートしていません。\nこの通知を無視して、別の端末から着信に応答することも、拒否することもできます。";
|
||||
// Crash report
|
||||
"google_analytics_use_prompt" = "匿名の誤動作報告と使用状況データを自動的に報告して%@の改善に役立てますか?";
|
||||
"google_analytics_use_prompt" = "匿名の誤動作報告と使用状況データを自動的に報告して%@の改善に役立てますか?";
|
||||
// Crypto
|
||||
"e2e_enabling_on_app_update" = "Riotはend-to-end暗号化をサポートするようになりましたが、再度有効にするにはログインする必要があります。\n\nアプリの設定から再ログインできます。今すぐ、または後からでも構いません。";
|
||||
"e2e_need_log_in_again" = "この端末のエンドツーエンド暗号鍵を生成し、接続先サーバに公開鍵を送信するには、再度ログインする必要があります。\n一度回線を切断します。ご不便おかけしてすみません。";
|
||||
@@ -498,9 +498,9 @@
|
||||
"settings_labs_room_members_lazy_loading_error_message" = "あなたのホームサーバーはまだルームメンバーの遅延ロードをサポートしていません。 後で試してください。";
|
||||
"settings_deactivate_my_account" = "アカウントを無効にします";
|
||||
"room_details_flair_section" = "コミュニティの特色を表示";
|
||||
"room_details_new_flair_placeholder" = "新しいコミュニティIDを追加 (例 +foo%@)";
|
||||
"room_details_new_flair_placeholder" = "新しいコミュニティIDを追加 (例 +foo%@)";
|
||||
"room_details_flair_invalid_id_prompt_title" = "無効な形式";
|
||||
"room_details_flair_invalid_id_prompt_msg" = "%@はコミュニティの有効な識別子ではありません";
|
||||
"room_details_flair_invalid_id_prompt_msg" = "%@はコミュニティの有効な識別子ではありません";
|
||||
"room_details_fail_to_update_room_communities" = "関連コミュニティを更新できない";
|
||||
// Group Details
|
||||
"group_details_title" = "コミュニティの詳細";
|
||||
@@ -512,15 +512,15 @@
|
||||
"group_home_multi_members_format" = "%tu メンバー";
|
||||
"group_home_one_room_format" = "1 部屋";
|
||||
"group_home_multi_rooms_format" = "%tu 部屋";
|
||||
"group_invitation_format" = "%@がこのコミュニティにあなたを招待しました";
|
||||
"group_invitation_format" = "%@がこのコミュニティにあなたを招待しました";
|
||||
// Group participants
|
||||
"group_participants_add_participant" = "参加者を追加";
|
||||
"group_participants_leave_prompt_title" = "グループを退出";
|
||||
"group_participants_leave_prompt_msg" = "本当にグループを退出しますか?";
|
||||
"group_participants_remove_prompt_title" = "確認";
|
||||
"group_participants_remove_prompt_msg" = "本当にこのグループから%@を削除しますか?";
|
||||
"group_participants_remove_prompt_msg" = "本当にこのグループから%@を削除しますか?";
|
||||
"group_participants_invite_prompt_title" = "確認";
|
||||
"group_participants_invite_prompt_msg" = "本当にこのグループに%@を招待しますか?";
|
||||
"group_participants_invite_prompt_msg" = "本当にこのグループに%@を招待しますか?";
|
||||
"group_participants_filter_members" = "コミュニティメンバーをフィルタリング";
|
||||
"group_participants_invite_another_user" = "ユーザーIDまたは名前による検索/招待";
|
||||
"group_participants_invite_malformed_id_title" = "招待エラー";
|
||||
@@ -535,13 +535,13 @@
|
||||
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "今すぐ追加しますか?";
|
||||
// Room key request dialog
|
||||
"e2e_room_key_request_title" = "暗号化キー要求";
|
||||
"e2e_room_key_request_message_new_device" = "暗号化キーを要求している新しい端末 '%@'を追加しました。";
|
||||
"e2e_room_key_request_message" = "検証されていない端末 '%@'が暗号化キーを要求しています。";
|
||||
"e2e_room_key_request_message_new_device" = "暗号化キーを要求している新しい端末 '%@'を追加しました。";
|
||||
"e2e_room_key_request_message" = "検証されていない端末 '%@'が暗号化キーを要求しています。";
|
||||
"e2e_room_key_request_start_verification" = "検証開始...";
|
||||
"e2e_room_key_request_share_without_verifying" = "検証せずに共有";
|
||||
"e2e_room_key_request_ignore_request" = "要求を無視";
|
||||
// GDPR
|
||||
"gdpr_consent_not_given_alert_message" = "%@ホームサーバーを引き続き使用するには、利用規約を確認して同意する必要があります。";
|
||||
"gdpr_consent_not_given_alert_message" = "%@ホームサーバーを引き続き使用するには、利用規約を確認して同意する必要があります。";
|
||||
"gdpr_consent_not_given_alert_review_now_action" = "今レビュー";
|
||||
"deactivate_account_title" = "無効なアカウント";
|
||||
"deactivate_account_informations_part1" = "これにより、アカウントは永久に使用できなくなります。 ログインすることはできず、誰も同じユーザーIDを再登録することはできません。 これにより、あなたのアカウントは参加しているすべての部屋から退去し、あなたのIDサーバーからアカウントの詳細が削除されます。 ";
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"auth_reset_password_email_validation_message" = "Een e-mail is naar %@ gestuurd. Zodra je de link die het bevat hebt gevolgd, klik hieronder.";
|
||||
"auth_reset_password_next_step_button" = "Ik heb mijn e-mailadres geverifieerd";
|
||||
"auth_reset_password_error_unauthorized" = "Het is niet gelukt om het e-mailadres te verifiëren: wees er zeker van dat je op de link in de e-mail hebt geklikt";
|
||||
"auth_reset_password_error_not_found" = "Het ziet er niet naar uit dat het e-mailadres met het Matrix ID op deze Homeserver is verbonden.";
|
||||
"auth_reset_password_error_not_found" = "Het ziet er niet naar uit dat het e-mailadres met het Matrix ID op deze homeserver is verbonden.";
|
||||
"auth_reset_password_success_message" = "Je wachtwoord is opnieuw ingesteld.\n\nJe bent op alle apparaten uitgelogd en je zal geen notificaties meer ontvangen. Om notificaties weer in te schakelen, log op elk apparaat opnieuw in.";
|
||||
"auth_add_email_and_phone_warning" = "Registratie met e-mail en telefoonnummer tegelijkertijd wordt nog niet ondersteund totdat de api bestaat. Alleen het telefoonnummer zal worden gebruikt. Je kan je e-mailadres later aan je profiel in de instellingen toevoegen.";
|
||||
// Chat creation
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"auth_reset_password_email_validation_message" = "An email has been sent to %@. Once you've followed the link it contains, click below.";
|
||||
"auth_reset_password_next_step_button" = "I have verified my email address";
|
||||
"auth_reset_password_error_unauthorized" = "Failed to verify email address: make sure you clicked the link in the email";
|
||||
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
|
||||
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this homeserver.";
|
||||
"auth_reset_password_success_message" = "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.";
|
||||
"auth_add_email_and_phone_warning" = "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.";
|
||||
"auth_accept_policies" = "Please review and accept the policies of this homeserver:";
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"auth_reset_password_email_validation_message" = "Email đã được gửi tới %@. Khi bạn đã theo liên kết trong đó, bấm vào dưới đây.";
|
||||
"auth_reset_password_next_step_button" = "Tôi đã xác thực địa chỉ email của tôi";
|
||||
"auth_reset_password_error_unauthorized" = "Xác thực địa chỉ email thất bại: hãy đảm bảo rằng bạn đã bấm vào địa chỉ đính kèm trong email";
|
||||
"auth_reset_password_error_not_found" = "Địa chỉ email có vẻ chưa được liên kết với Matrix ID trên Homeserver này.";
|
||||
"auth_reset_password_error_not_found" = "Địa chỉ email có vẻ chưa được liên kết với Matrix ID trên homeserver này.";
|
||||
"auth_reset_password_success_message" = "Mật khẩu của bạn đã được thiết lập lại.\n\nBạn đã được đăng xuất khỏi tất cả các thiết bị và sẽ không còn nhận được thông báo. Để bật lại thông báo, đăng nhập lại trên mỗi thiết bị.";
|
||||
"auth_add_email_and_phone_warning" = "Đăng kí với mật khẩu và số điện thoại cùng lúc chưa được hỗ trợ cho tới khi api được thiết lập. Duy nhất số điện thoại sẽ được liên kết với với tài khoản. Bạn sẽ phải thêm email vào hồ sơ trong mục cài đặt.";
|
||||
// Chat creation
|
||||
|
||||
@@ -577,7 +577,7 @@
|
||||
// Share extension
|
||||
"share_extension_auth_prompt" = "登录主应用程序以共享内容";
|
||||
"share_extension_failed_to_encrypt" = "发送失败。 请在主应用程序中检查此房间的加密设置";
|
||||
"e2e_room_key_request_message_new_device" = "您添加了一个新设备 '%@' ,它正在请求加密密钥。";
|
||||
"e2e_room_key_request_message_new_device" = "您添加了一个新设备 '%@' ,它正在请求加密密钥。";
|
||||
"e2e_room_key_request_message" = "您的未验证设备 '%@' 正在请求加密密钥。";
|
||||
// GDPR
|
||||
"gdpr_consent_not_given_alert_message" = "要继续使用该 %@ 主服务器,您必须查看并同意其服务条款和条件。";
|
||||
|
||||
@@ -213,9 +213,9 @@
|
||||
"search_in_progress" = "搜尋中……";
|
||||
// Directory
|
||||
"directory_cell_title" = "瀏覽目錄";
|
||||
"directory_cell_description" = "%tu 個聊天室";
|
||||
"directory_cell_description" = "%tu 個聊天室";
|
||||
"directory_search_results_title" = "聊天室目錄搜尋結果";
|
||||
"directory_search_results" = "搜尋 %@ 有 %tu 個結果";
|
||||
"directory_search_results" = "搜尋 %@ 有 %tu 個結果";
|
||||
"directory_search_results_more_than" = "搜尋 %@ 有超過 %tu 個結果";
|
||||
"directory_searching_title" = "搜尋聊天室目錄中……";
|
||||
"directory_search_fail" = "無法取得資料";
|
||||
@@ -437,7 +437,7 @@
|
||||
"group_home_one_member_format" = "1 位成員";
|
||||
"group_home_multi_members_format" = "%tu 位成員";
|
||||
"group_home_one_room_format" = "一個聊天室";
|
||||
"group_home_multi_rooms_format" = "%tu 個聊天室";
|
||||
"group_home_multi_rooms_format" = "%tu 個聊天室";
|
||||
"group_invitation_format" = "%@ 邀請您加入此社群";
|
||||
// Group participants
|
||||
"group_participants_add_participant" = "新增成員";
|
||||
|
||||
@@ -22,6 +22,7 @@ internal enum RiotDefaults {
|
||||
internal static let integrationsRestUrl: String = _document["integrationsRestUrl"]
|
||||
internal static let integrationsUiUrl: String = _document["integrationsUiUrl"]
|
||||
internal static let integrationsWidgetsUrls: [String] = _document["integrationsWidgetsUrls"]
|
||||
internal static let jitsiServerURL: String = _document["jitsiServerURL"]
|
||||
internal static let matrixApps: Bool = _document["matrixApps"]
|
||||
internal static let maxAllowedMediaCacheSize: Int = _document["maxAllowedMediaCacheSize"]
|
||||
internal static let pinRoomsWithMissedNotif: Bool = _document["pinRoomsWithMissedNotif"]
|
||||
|
||||
@@ -12,6 +12,31 @@ import UIKit
|
||||
|
||||
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||
internal enum StoryboardScene {
|
||||
internal enum DeviceVerificationDataLoadingViewController: StoryboardType {
|
||||
internal static let storyboardName = "DeviceVerificationDataLoadingViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.DeviceVerificationDataLoadingViewController>(storyboard: DeviceVerificationDataLoadingViewController.self)
|
||||
}
|
||||
internal enum DeviceVerificationIncomingViewController: StoryboardType {
|
||||
internal static let storyboardName = "DeviceVerificationIncomingViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.DeviceVerificationIncomingViewController>(storyboard: DeviceVerificationIncomingViewController.self)
|
||||
}
|
||||
internal enum DeviceVerificationStartViewController: StoryboardType {
|
||||
internal static let storyboardName = "DeviceVerificationStartViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.DeviceVerificationStartViewController>(storyboard: DeviceVerificationStartViewController.self)
|
||||
}
|
||||
internal enum DeviceVerificationVerifiedViewController: StoryboardType {
|
||||
internal static let storyboardName = "DeviceVerificationVerifiedViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.DeviceVerificationVerifiedViewController>(storyboard: DeviceVerificationVerifiedViewController.self)
|
||||
}
|
||||
internal enum DeviceVerificationVerifyViewController: StoryboardType {
|
||||
internal static let storyboardName = "DeviceVerificationVerifyViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.DeviceVerificationVerifyViewController>(storyboard: DeviceVerificationVerifyViewController.self)
|
||||
}
|
||||
internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType {
|
||||
internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController"
|
||||
|
||||
@@ -47,6 +72,16 @@ internal enum StoryboardScene {
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromRecoveryKeyViewController>(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self)
|
||||
}
|
||||
internal enum SimpleScreenTemplateViewController: StoryboardType {
|
||||
internal static let storyboardName = "SimpleScreenTemplateViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.SimpleScreenTemplateViewController>(storyboard: SimpleScreenTemplateViewController.self)
|
||||
}
|
||||
internal enum TemplateScreenViewController: StoryboardType {
|
||||
internal static let storyboardName = "TemplateScreenViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.TemplateScreenViewController>(storyboard: TemplateScreenViewController.self)
|
||||
}
|
||||
}
|
||||
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||
|
||||
|
||||
+355
-19
@@ -38,7 +38,7 @@ internal enum VectorL10n {
|
||||
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.
|
||||
/// Add an email address to your account to let users discover you, and to reset your password.
|
||||
internal static var authAddEmailMessage: String {
|
||||
return VectorL10n.tr("Vector", "auth_add_email_message")
|
||||
}
|
||||
@@ -166,7 +166,7 @@ internal enum VectorL10n {
|
||||
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
|
||||
/// This homeserver would like to make sure you are not a robot
|
||||
internal static var authRecaptchaMessage: String {
|
||||
return VectorL10n.tr("Vector", "auth_recaptcha_message")
|
||||
}
|
||||
@@ -186,7 +186,7 @@ internal enum VectorL10n {
|
||||
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.
|
||||
/// 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")
|
||||
}
|
||||
@@ -426,6 +426,342 @@ internal enum VectorL10n {
|
||||
internal static var decline: String {
|
||||
return VectorL10n.tr("Vector", "decline")
|
||||
}
|
||||
/// The other party cancelled the verification.
|
||||
internal static var deviceVerificationCancelled: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_cancelled")
|
||||
}
|
||||
/// The verification has been cancelled. Reason: %@
|
||||
internal static func deviceVerificationCancelledByMe(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "device_verification_cancelled_by_me", p1)
|
||||
}
|
||||
/// Aeroplane
|
||||
internal static var deviceVerificationEmojiAeroplane: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_aeroplane")
|
||||
}
|
||||
/// Anchor
|
||||
internal static var deviceVerificationEmojiAnchor: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_anchor")
|
||||
}
|
||||
/// Apple
|
||||
internal static var deviceVerificationEmojiApple: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_apple")
|
||||
}
|
||||
/// Ball
|
||||
internal static var deviceVerificationEmojiBall: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_ball")
|
||||
}
|
||||
/// Banana
|
||||
internal static var deviceVerificationEmojiBanana: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_banana")
|
||||
}
|
||||
/// Ball
|
||||
internal static var deviceVerificationEmojiBell: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_bell")
|
||||
}
|
||||
/// Bicycle
|
||||
internal static var deviceVerificationEmojiBicycle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_bicycle")
|
||||
}
|
||||
/// Book
|
||||
internal static var deviceVerificationEmojiBook: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_book")
|
||||
}
|
||||
/// Butterfly
|
||||
internal static var deviceVerificationEmojiButterfly: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_butterfly")
|
||||
}
|
||||
/// Cactus
|
||||
internal static var deviceVerificationEmojiCactus: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_cactus")
|
||||
}
|
||||
/// Cake
|
||||
internal static var deviceVerificationEmojiCake: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_cake")
|
||||
}
|
||||
/// Cat
|
||||
internal static var deviceVerificationEmojiCat: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_cat")
|
||||
}
|
||||
/// Class
|
||||
internal static var deviceVerificationEmojiClock: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_clock")
|
||||
}
|
||||
/// Cloud
|
||||
internal static var deviceVerificationEmojiCloud: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_cloud")
|
||||
}
|
||||
/// Corn
|
||||
internal static var deviceVerificationEmojiCorn: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_corn")
|
||||
}
|
||||
/// Dog
|
||||
internal static var deviceVerificationEmojiDog: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_dog")
|
||||
}
|
||||
/// Elephant
|
||||
internal static var deviceVerificationEmojiElephant: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_elephant")
|
||||
}
|
||||
/// Fire
|
||||
internal static var deviceVerificationEmojiFire: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_fire")
|
||||
}
|
||||
/// Fish
|
||||
internal static var deviceVerificationEmojiFish: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_fish")
|
||||
}
|
||||
/// Flag
|
||||
internal static var deviceVerificationEmojiFlag: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_flag")
|
||||
}
|
||||
/// Flower
|
||||
internal static var deviceVerificationEmojiFlower: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_flower")
|
||||
}
|
||||
/// Folder
|
||||
internal static var deviceVerificationEmojiFolder: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_folder")
|
||||
}
|
||||
/// Gift
|
||||
internal static var deviceVerificationEmojiGift: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_gift")
|
||||
}
|
||||
/// Glasses
|
||||
internal static var deviceVerificationEmojiGlasses: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_glasses")
|
||||
}
|
||||
/// Globe
|
||||
internal static var deviceVerificationEmojiGlobe: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_globe")
|
||||
}
|
||||
/// Guitar
|
||||
internal static var deviceVerificationEmojiGuitar: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_guitar")
|
||||
}
|
||||
/// Hammer
|
||||
internal static var deviceVerificationEmojiHammer: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_hammer")
|
||||
}
|
||||
/// Hat
|
||||
internal static var deviceVerificationEmojiHat: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_hat")
|
||||
}
|
||||
/// Headphones
|
||||
internal static var deviceVerificationEmojiHeadphones: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_headphones")
|
||||
}
|
||||
/// Heart
|
||||
internal static var deviceVerificationEmojiHeart: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_heart")
|
||||
}
|
||||
/// Horse
|
||||
internal static var deviceVerificationEmojiHorse: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_horse")
|
||||
}
|
||||
/// Hourglass
|
||||
internal static var deviceVerificationEmojiHourglass: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_hourglass")
|
||||
}
|
||||
/// Key
|
||||
internal static var deviceVerificationEmojiKey: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_key")
|
||||
}
|
||||
/// Light Bulb
|
||||
internal static var deviceVerificationEmojiLightBulb: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_light bulb")
|
||||
}
|
||||
/// Lion
|
||||
internal static var deviceVerificationEmojiLion: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_lion")
|
||||
}
|
||||
/// Moon
|
||||
internal static var deviceVerificationEmojiMoon: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_moon")
|
||||
}
|
||||
/// Mushroom
|
||||
internal static var deviceVerificationEmojiMushroom: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_mushroom")
|
||||
}
|
||||
/// Octopus
|
||||
internal static var deviceVerificationEmojiOctopus: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_octopus")
|
||||
}
|
||||
/// Padlock
|
||||
internal static var deviceVerificationEmojiPadlock: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_padlock")
|
||||
}
|
||||
/// Panda
|
||||
internal static var deviceVerificationEmojiPanda: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_panda")
|
||||
}
|
||||
/// Paperclip
|
||||
internal static var deviceVerificationEmojiPaperclip: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_paperclip")
|
||||
}
|
||||
/// Pencil
|
||||
internal static var deviceVerificationEmojiPencil: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_pencil")
|
||||
}
|
||||
/// Penguin
|
||||
internal static var deviceVerificationEmojiPenguin: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_penguin")
|
||||
}
|
||||
/// Pig
|
||||
internal static var deviceVerificationEmojiPig: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_pig")
|
||||
}
|
||||
/// Pin
|
||||
internal static var deviceVerificationEmojiPin: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_pin")
|
||||
}
|
||||
/// Pizza
|
||||
internal static var deviceVerificationEmojiPizza: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_pizza")
|
||||
}
|
||||
/// Rabbit
|
||||
internal static var deviceVerificationEmojiRabbit: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_rabbit")
|
||||
}
|
||||
/// Robot
|
||||
internal static var deviceVerificationEmojiRobot: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_robot")
|
||||
}
|
||||
/// Rocket
|
||||
internal static var deviceVerificationEmojiRocket: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_rocket")
|
||||
}
|
||||
/// Rooster
|
||||
internal static var deviceVerificationEmojiRooster: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_rooster")
|
||||
}
|
||||
/// Santa
|
||||
internal static var deviceVerificationEmojiSanta: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_santa")
|
||||
}
|
||||
/// Scissors
|
||||
internal static var deviceVerificationEmojiScissors: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_scissors")
|
||||
}
|
||||
/// Smiley
|
||||
internal static var deviceVerificationEmojiSmiley: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_smiley")
|
||||
}
|
||||
/// Spanner
|
||||
internal static var deviceVerificationEmojiSpanner: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_spanner")
|
||||
}
|
||||
/// Strawberry
|
||||
internal static var deviceVerificationEmojiStrawberry: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_strawberry")
|
||||
}
|
||||
/// Telephone
|
||||
internal static var deviceVerificationEmojiTelephone: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_telephone")
|
||||
}
|
||||
/// Thumbs up
|
||||
internal static var deviceVerificationEmojiThumbsUp: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_thumbs up")
|
||||
}
|
||||
/// Train
|
||||
internal static var deviceVerificationEmojiTrain: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_train")
|
||||
}
|
||||
/// Tree
|
||||
internal static var deviceVerificationEmojiTree: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_tree")
|
||||
}
|
||||
/// Trophy
|
||||
internal static var deviceVerificationEmojiTrophy: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_trophy")
|
||||
}
|
||||
/// Trumpet
|
||||
internal static var deviceVerificationEmojiTrumpet: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_trumpet")
|
||||
}
|
||||
/// Turtle
|
||||
internal static var deviceVerificationEmojiTurtle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_turtle")
|
||||
}
|
||||
/// Umbrella
|
||||
internal static var deviceVerificationEmojiUmbrella: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_umbrella")
|
||||
}
|
||||
/// Unicorn
|
||||
internal static var deviceVerificationEmojiUnicorn: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_emoji_unicorn")
|
||||
}
|
||||
/// Cannot load device information.
|
||||
internal static var deviceVerificationErrorCannotLoadDevice: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_error_cannot_load_device")
|
||||
}
|
||||
/// Verify this device to mark it as trusted. Trusting devices of partners gives you extra peace of mind when using end-to-end encrypted messages.
|
||||
internal static var deviceVerificationIncomingDescription1: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_incoming_description_1")
|
||||
}
|
||||
/// Verifying this device will mark it as trusted, and also mark your device as trusted to the partner.
|
||||
internal static var deviceVerificationIncomingDescription2: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_incoming_description_2")
|
||||
}
|
||||
/// Incoming Verification Request
|
||||
internal static var deviceVerificationIncomingTitle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_incoming_title")
|
||||
}
|
||||
/// For maximum security, we recommend you do this in person or use another trusted means of communication
|
||||
internal static var deviceVerificationSecurityAdvice: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_security_advice")
|
||||
}
|
||||
/// Verify by comparing a short text string
|
||||
internal static var deviceVerificationStartTitle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_start_title")
|
||||
}
|
||||
/// Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification.
|
||||
internal static var deviceVerificationStartUseLegacy: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_start_use_legacy")
|
||||
}
|
||||
/// Use Legacy Verification
|
||||
internal static var deviceVerificationStartUseLegacyAction: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_start_use_legacy_action")
|
||||
}
|
||||
/// Begin Verifying
|
||||
internal static var deviceVerificationStartVerifyButton: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_start_verify_button")
|
||||
}
|
||||
/// Waiting for partner to accept...
|
||||
internal static var deviceVerificationStartWaitPartner: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_start_wait_partner")
|
||||
}
|
||||
/// Verify device
|
||||
internal static var deviceVerificationTitle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_title")
|
||||
}
|
||||
/// You've successfully verified this device.
|
||||
internal static var deviceVerificationVerifiedDescription1: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verified_description_1")
|
||||
}
|
||||
/// Secure messages with this user are end-to-end encrypted and not able to be read by third parties.
|
||||
internal static var deviceVerificationVerifiedDescription2: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verified_description_2")
|
||||
}
|
||||
/// Got it
|
||||
internal static var deviceVerificationVerifiedGotItButton: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verified_got_it_button")
|
||||
}
|
||||
/// Verified!
|
||||
internal static var deviceVerificationVerifiedTitle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verified_title")
|
||||
}
|
||||
/// Verify this device by confirming the following emoji appear on the screen of the partner
|
||||
internal static var deviceVerificationVerifyTitleEmoji: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verify_title_emoji")
|
||||
}
|
||||
/// Verify this device by confirming the following numbers appear on the screen of the partner
|
||||
internal static var deviceVerificationVerifyTitleNumber: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verify_title_number")
|
||||
}
|
||||
/// Waiting for partner to confirm...
|
||||
internal static var deviceVerificationVerifyWaitPartner: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_verify_wait_partner")
|
||||
}
|
||||
/// %tu rooms
|
||||
internal static func directoryCellDescription(_ p1: Int) -> String {
|
||||
return VectorL10n.tr("Vector", "directory_cell_description", p1)
|
||||
@@ -686,14 +1022,14 @@ internal enum VectorL10n {
|
||||
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")
|
||||
}
|
||||
/// Connect this device to Key Backup
|
||||
internal static var keyBackupRecoverConnentBannerSubtitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_recover_connent_banner_subtitle")
|
||||
}
|
||||
/// Done
|
||||
internal static var keyBackupRecoverDoneAction: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_recover_done_action")
|
||||
@@ -790,14 +1126,14 @@ internal enum VectorL10n {
|
||||
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")
|
||||
}
|
||||
/// Connect this device to Key Backup
|
||||
internal static var keyBackupSetupIntroSetupConnectActionWithExistingBackup: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_setup_intro_setup_connect_action_with_existing_backup")
|
||||
}
|
||||
/// Never lose encrypted messages
|
||||
internal static var keyBackupSetupIntroTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_setup_intro_title")
|
||||
@@ -1878,7 +2214,7 @@ internal enum VectorL10n {
|
||||
internal static var settingsClearCache: String {
|
||||
return VectorL10n.tr("Vector", "settings_clear_cache")
|
||||
}
|
||||
/// Home server is %@
|
||||
/// Homeserver is %@
|
||||
internal static func settingsConfigHomeServer(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "settings_config_home_server", p1)
|
||||
}
|
||||
@@ -2006,6 +2342,10 @@ internal enum VectorL10n {
|
||||
internal static var settingsKeyBackup: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup")
|
||||
}
|
||||
/// Connect this device to Key Backup
|
||||
internal static var settingsKeyBackupButtonConnect: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup_button_connect")
|
||||
}
|
||||
/// Start using Key Backup
|
||||
internal static var settingsKeyBackupButtonCreate: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup_button_create")
|
||||
@@ -2018,10 +2358,6 @@ internal enum VectorL10n {
|
||||
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")
|
||||
@@ -2046,7 +2382,7 @@ internal enum VectorL10n {
|
||||
internal static var settingsKeyBackupInfoNone: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup_info_none")
|
||||
}
|
||||
/// This device is not backing up your keys.
|
||||
/// This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.
|
||||
internal static var settingsKeyBackupInfoNotValid: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup_info_not_valid")
|
||||
}
|
||||
@@ -2058,7 +2394,7 @@ internal enum VectorL10n {
|
||||
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.
|
||||
/// Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.
|
||||
internal static var settingsKeyBackupInfoSignoutWarning: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup_info_signout_warning")
|
||||
}
|
||||
@@ -2488,7 +2824,7 @@ internal enum VectorL10n {
|
||||
// MARK: - Implementation Details
|
||||
|
||||
extension VectorL10n {
|
||||
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||
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() {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
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 Themable: class {
|
||||
func update(theme: Theme)
|
||||
}
|
||||
@@ -102,7 +102,7 @@
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="AuthenticationVCActivityIndicator"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</activityIndicatorView>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Currently we do not support authentication flows defined by this Home Server" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="54b-4O-ip9" userLabel="noFlowLabel">
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Currently we do not support authentication flows defined by this homeserver" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="54b-4O-ip9" userLabel="noFlowLabel">
|
||||
<rect key="frame" x="28" y="8" width="319.33333333333331" height="33.666666666666664"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
@@ -226,7 +226,7 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1YY-gb-LG4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="70"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Home Server:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kHf-s1-cZa">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Homeserver:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kHf-s1-cZa">
|
||||
<rect key="frame" x="18" y="8" width="339" height="20"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCHSLabel"/>
|
||||
<constraints>
|
||||
|
||||
@@ -848,7 +848,7 @@
|
||||
|
||||
if (restClient)
|
||||
{
|
||||
// Sanity check on home server
|
||||
// Sanity check on homeserver
|
||||
id hs_url = registrationParameters[@"hs_url"];
|
||||
if (hs_url && [hs_url isKindOfClass:NSString.class])
|
||||
{
|
||||
|
||||
@@ -759,8 +759,8 @@
|
||||
{
|
||||
// Avoid multiple openings of rooms
|
||||
self.userInteractionEnabled = NO;
|
||||
|
||||
[[AppDelegate theDelegate].masterTabBarController selectRoomWithId:roomId andEventId:nil inMatrixSession:matrixSession completion:^{
|
||||
|
||||
[[AppDelegate theDelegate] showRoom:roomId andEventId:nil withMatrixSession:matrixSession restoreInitialDisplay:NO completion:^{
|
||||
self.userInteractionEnabled = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -125,28 +125,14 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
||||
}
|
||||
|
||||
// Use bold font for the room title
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.roomTitle.font = [UIFont systemFontOfSize:17 weight:UIFontWeightBold];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomTitle.font = [UIFont boldSystemFontOfSize:17];
|
||||
}
|
||||
self.roomTitle.font = [UIFont systemFontOfSize:17 weight:UIFontWeightBold];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.lastEventDate.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
|
||||
// The room title is not bold anymore
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.roomTitle.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomTitle.font = [UIFont systemFontOfSize:17];
|
||||
}
|
||||
// The room title is not bold anymore
|
||||
self.roomTitle.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium];
|
||||
}
|
||||
|
||||
self.directRoomBorderView.hidden = !roomCellData.roomSummary.room.isDirect;
|
||||
|
||||
@@ -859,7 +859,7 @@
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[GroupHomeViewController] Error: The home server failed to resolve the room alias (%@)", roomIdOrAlias);
|
||||
NSLog(@"[GroupHomeViewController] Error: The homeserver failed to resolve the room alias (%@)", roomIdOrAlias);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationCoordinator: DeviceVerificationCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let navigationRouter: NavigationRouterType
|
||||
private let session: MXSession
|
||||
private let otherUserId: String
|
||||
private let otherDeviceId: String
|
||||
|
||||
private var incomingTransaction: MXIncomingSASTransaction?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: DeviceVerificationCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// Contrustor to start a verification of another device.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - session: the MXSession
|
||||
/// - otherUserId: the device user id
|
||||
/// - otherDevice: the device id
|
||||
init(session: MXSession, otherUserId: String, otherDeviceId: String) {
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.session = session
|
||||
self.otherUserId = otherUserId
|
||||
self.otherDeviceId = otherDeviceId
|
||||
}
|
||||
|
||||
/// Contrustor to manage an incoming SAS device verification transaction
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - session: the MXSession
|
||||
/// - transaction: an existing device verification transaction
|
||||
convenience init(session: MXSession, incomingTransaction: MXIncomingSASTransaction) {
|
||||
self.init(session: session,
|
||||
otherUserId: incomingTransaction.otherUserId,
|
||||
otherDeviceId: incomingTransaction.otherDeviceId)
|
||||
self.incomingTransaction = incomingTransaction
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let rootCoordinator = self.createDataLoadingScreenCoordinator()
|
||||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
self.navigationRouter.setRootModule(rootCoordinator)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func createDataLoadingScreenCoordinator() -> DeviceVerificationDataLoadingCoordinator {
|
||||
let coordinator = DeviceVerificationDataLoadingCoordinator(session: self.session, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func showStart(otherUser: MXUser, otherDevice: MXDeviceInfo) {
|
||||
let coordinator = DeviceVerificationStartCoordinator(session: self.session, otherUser: otherUser, otherDevice: otherDevice)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.setRootModule(coordinator)
|
||||
}
|
||||
|
||||
private func showIncoming(otherUser: MXUser, transaction: MXIncomingSASTransaction) {
|
||||
let coordinator = DeviceVerificationIncomingCoordinator(session: self.session, otherUser: otherUser, transaction: transaction)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.setRootModule(coordinator)
|
||||
}
|
||||
|
||||
private func showVerify(transaction: MXSASTransaction, animated: Bool) {
|
||||
let coordinator = DeviceVerificationVerifyCoordinator(session: self.session, transaction: transaction)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.push(coordinator, animated: animated) { [weak self] in
|
||||
self?.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func showVerified(animated: Bool) {
|
||||
let viewController = DeviceVerificationVerifiedViewController.instantiate()
|
||||
viewController.delegate = self
|
||||
self.navigationRouter.setRootModule(viewController)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: DeviceVerificationDataLoadingCoordinatorDelegate {
|
||||
func deviceVerificationDataLoadingCoordinator(_ coordinator: DeviceVerificationDataLoadingCoordinatorType, didLoadUser user: MXUser, device: MXDeviceInfo) {
|
||||
|
||||
if let incomingTransaction = self.incomingTransaction {
|
||||
self.showIncoming(otherUser: user, transaction: incomingTransaction)
|
||||
} else {
|
||||
self.showStart(otherUser: user, otherDevice: device)
|
||||
}
|
||||
}
|
||||
|
||||
func deviceVerificationDataLoadingCoordinatorDidCancel(_ coordinator: DeviceVerificationDataLoadingCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate {
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
||||
self.showVerify(transaction: transaction, animated: true)
|
||||
}
|
||||
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
|
||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: DeviceVerificationIncomingCoordinatorDelegate {
|
||||
func deviceVerificationIncomingCoordinator(_ coordinator: DeviceVerificationIncomingCoordinatorType, didAcceptTransaction transaction: MXSASTransaction) {
|
||||
self.showVerify(transaction: transaction, animated: true)
|
||||
}
|
||||
|
||||
func deviceVerificationIncomingCoordinatorDidCancel(_ coordinator: DeviceVerificationIncomingCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: DeviceVerificationVerifyCoordinatorDelegate {
|
||||
func deviceVerificationVerifyCoordinatorDidComplete(_ coordinator: DeviceVerificationVerifyCoordinatorType) {
|
||||
self.showVerified(animated: true)
|
||||
}
|
||||
|
||||
func deviceVerificationVerifyCoordinatorDidCancel(_ coordinator: DeviceVerificationVerifyCoordinatorType) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceVerificationCoordinator: DeviceVerificationVerifiedViewControllerDelegate {
|
||||
func deviceVerificationVerifiedViewControllerDidTapSetupAction(_ viewController: DeviceVerificationVerifiedViewController) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
|
||||
func deviceVerificationVerifiedViewControllerDidCancel(_ viewController: DeviceVerificationVerifiedViewController) {
|
||||
self.delegate?.deviceVerificationCoordinatorDidComplete(self, otherUserId: self.otherUserId, otherDeviceId: self.otherDeviceId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationCoordinatorBridgePresenterDelegate {
|
||||
func deviceVerificationCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: DeviceVerificationCoordinatorBridgePresenter, otherUserId: String, otherDeviceId: String)
|
||||
}
|
||||
|
||||
/// DeviceVerificationCoordinatorBridgePresenter enables to start DeviceVerificationCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var coordinator: DeviceVerificationCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: DeviceVerificationCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// 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, animated: animated)
|
||||
// }
|
||||
|
||||
func present(from viewController: UIViewController, otherUserId: String, otherDeviceId: String, animated: Bool) {
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func present(from viewController: UIViewController, incomingTransaction: MXIncomingSASTransaction, animated: Bool) {
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, incomingTransaction: incomingTransaction)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
deviceVerificationCoordinator.start()
|
||||
|
||||
self.coordinator = deviceVerificationCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationCoordinatorDelegate
|
||||
extension DeviceVerificationCoordinatorBridgePresenter: DeviceVerificationCoordinatorDelegate {
|
||||
func deviceVerificationCoordinatorDidComplete(_ coordinator: DeviceVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
||||
self.delegate?.deviceVerificationCoordinatorBridgePresenterDelegateDidComplete(self, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationCoordinatorDelegate: class {
|
||||
func deviceVerificationCoordinatorDidComplete(_ coordinator: DeviceVerificationCoordinatorType, otherUserId: String, otherDeviceId: String)
|
||||
}
|
||||
|
||||
/// `DeviceVerificationCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
|
||||
protocol DeviceVerificationCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: DeviceVerificationCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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 DeviceVerificationIncomingCoordinator: DeviceVerificationIncomingCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var deviceVerificationIncomingViewModel: DeviceVerificationIncomingViewModelType
|
||||
private let deviceVerificationIncomingViewController: DeviceVerificationIncomingViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: DeviceVerificationIncomingCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUser: MXUser, transaction: MXIncomingSASTransaction) {
|
||||
self.session = session
|
||||
|
||||
let deviceVerificationIncomingViewModel = DeviceVerificationIncomingViewModel(session: self.session, otherUser: otherUser, transaction: transaction)
|
||||
let deviceVerificationIncomingViewController = DeviceVerificationIncomingViewController.instantiate(with: deviceVerificationIncomingViewModel)
|
||||
self.deviceVerificationIncomingViewModel = deviceVerificationIncomingViewModel
|
||||
self.deviceVerificationIncomingViewController = deviceVerificationIncomingViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.deviceVerificationIncomingViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.deviceVerificationIncomingViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationIncomingViewModelCoordinatorDelegate
|
||||
extension DeviceVerificationIncomingCoordinator: DeviceVerificationIncomingViewModelCoordinatorDelegate {
|
||||
|
||||
func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didAcceptTransaction transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationIncomingCoordinator(self, didAcceptTransaction: transaction)
|
||||
}
|
||||
|
||||
func deviceVerificationIncomingViewModelDidCancel(_ viewModel: DeviceVerificationIncomingViewModelType) {
|
||||
self.delegate?.deviceVerificationIncomingCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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 DeviceVerificationIncomingCoordinatorDelegate: class {
|
||||
func deviceVerificationIncomingCoordinator(_ coordinator: DeviceVerificationIncomingCoordinatorType, didAcceptTransaction message: MXSASTransaction)
|
||||
func deviceVerificationIncomingCoordinatorDidCancel(_ coordinator: DeviceVerificationIncomingCoordinatorType)
|
||||
}
|
||||
|
||||
/// `DeviceVerificationIncomingCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol DeviceVerificationIncomingCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: DeviceVerificationIncomingCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationIncomingViewController view actions exposed to view model
|
||||
enum DeviceVerificationIncomingViewAction {
|
||||
case accept
|
||||
case cancel
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Device Verification Incoming View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="DeviceVerificationIncomingViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="528"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="528"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Incoming Verification Request" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="he8-pl-xE9">
|
||||
<rect key="frame" x="20" y="35" width="335" height="41"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="41" id="Nam-ca-50k"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="favourite" translatesAutoresizingMaskIntoConstraints="NO" id="4aN-Cq-vqG" customClass="MXKImageView">
|
||||
<rect key="frame" x="155.5" y="86" width="64" height="64"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="64" id="BSb-a6-GeY"/>
|
||||
<constraint firstAttribute="height" constant="64" id="gML-vd-Y0C"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="User Displayname" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZIv-LS-3Mg">
|
||||
<rect key="frame" x="20" y="158" width="335" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="DEVICEID" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MpQ-jR-g1m">
|
||||
<rect key="frame" x="20" y="187" width="335" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verifying this device will mark it as trusted, and also mark your device as trusted to the partner." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6yX-xD-4X5">
|
||||
<rect key="frame" x="20" y="228" width="335" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="80" id="pta-eP-0yH"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
|
||||
<rect key="frame" x="20" y="300" width="335" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="80" id="Y2u-fJ-idS"/>
|
||||
</constraints>
|
||||
<string key="text">Verify this device to mark it as trusted. Trusting devices of partners gives you extra peace of mind when using end-to-end encrypted messages.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DtR-jx-UKY">
|
||||
<rect key="frame" x="0.0" y="400" width="375" height="50"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
|
||||
<rect key="frame" x="20" y="10" width="335" height="30"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<state key="normal" title="Continue">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="continueButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="cMi-C9-iIh"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerY" secondItem="DtR-jx-UKY" secondAttribute="centerY" id="5eX-a5-zpP"/>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerX" secondItem="DtR-jx-UKY" secondAttribute="centerX" id="6v9-MN-mk2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DOt-5E-FjF" secondAttribute="trailing" constant="20" id="7Cr-6b-kGn"/>
|
||||
<constraint firstAttribute="height" constant="50" id="QNq-au-ZdL"/>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="leading" secondItem="DtR-jx-UKY" secondAttribute="leading" constant="20" id="YrM-nf-AuH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="bottom" secondItem="bxI-mu-qng" secondAttribute="top" constant="8" id="6Bh-e4-HVr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DtR-jx-UKY" secondAttribute="trailing" id="K7y-Df-dgz"/>
|
||||
<constraint firstItem="ZIv-LS-3Mg" firstAttribute="leading" secondItem="he8-pl-xE9" secondAttribute="leading" id="OYT-Q7-yDa"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="bxI-mu-qng" secondAttribute="leading" id="Q9n-7p-gHl"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="QSg-yz-aaB"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="Rg4-jV-Nht"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="20" id="Smz-GL-JdW"/>
|
||||
<constraint firstItem="ZIv-LS-3Mg" firstAttribute="trailing" secondItem="he8-pl-xE9" secondAttribute="trailing" id="TZv-Rd-OBF"/>
|
||||
<constraint firstItem="4aN-Cq-vqG" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="UBd-6J-ZuW"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ZP8-mV-RBh"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" id="Zeg-U8-uis"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="leading" secondItem="he8-pl-xE9" secondAttribute="leading" id="bma-QK-Hut"/>
|
||||
<constraint firstItem="MpQ-jR-g1m" firstAttribute="top" secondItem="ZIv-LS-3Mg" secondAttribute="bottom" constant="8" id="cbT-st-5j5"/>
|
||||
<constraint firstItem="ZIv-LS-3Mg" firstAttribute="top" secondItem="4aN-Cq-vqG" secondAttribute="bottom" constant="8" id="fiq-v2-5EA"/>
|
||||
<constraint firstItem="4aN-Cq-vqG" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="bottom" constant="10" id="g4o-2j-hEe"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="375" id="glD-Sz-73O"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="hK0-aA-d9H"/>
|
||||
<constraint firstItem="MpQ-jR-g1m" firstAttribute="leading" secondItem="he8-pl-xE9" secondAttribute="leading" id="hMj-hb-HYg"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="trailing" secondItem="he8-pl-xE9" secondAttribute="trailing" id="mYW-nM-bvw"/>
|
||||
<constraint firstItem="MpQ-jR-g1m" firstAttribute="trailing" secondItem="he8-pl-xE9" secondAttribute="trailing" id="qbA-ez-dYG"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="35" id="s3k-Io-834"/>
|
||||
<constraint firstAttribute="bottom" secondItem="DtR-jx-UKY" secondAttribute="bottom" constant="78" id="vi4-yP-gLN"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="top" secondItem="MpQ-jR-g1m" secondAttribute="bottom" constant="20" id="ymb-VV-LiR"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" constant="70" id="Y46-NP-zAc"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="avatarImageView" destination="4aN-Cq-vqG" id="rDT-ne-J1g"/>
|
||||
<outlet property="continueButton" destination="DOt-5E-FjF" id="EHR-Zq-tH5"/>
|
||||
<outlet property="continueButtonBackgroundView" destination="DtR-jx-UKY" id="hc4-9Z-HeC"/>
|
||||
<outlet property="description1Label" destination="6yX-xD-4X5" id="KV5-9h-BhU"/>
|
||||
<outlet property="description2Label" destination="bxI-mu-qng" id="5bW-iT-67c"/>
|
||||
<outlet property="deviceIdLabel" destination="MpQ-jR-g1m" id="KSN-yf-zFB"/>
|
||||
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
|
||||
<outlet property="titleLabel" destination="he8-pl-xE9" id="4SL-xX-ETK"/>
|
||||
<outlet property="userDisplaynameLabel" destination="ZIv-LS-3Mg" id="pHg-qH-yUb"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3199.1999999999998" y="-647.22638680659679"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="favourite" width="32.5" height="28.5"/>
|
||||
</resources>
|
||||
</document>
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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 DeviceVerificationIncomingViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
|
||||
@IBOutlet weak var avatarImageView: MXKImageView!
|
||||
@IBOutlet weak var userDisplaynameLabel: UILabel!
|
||||
@IBOutlet weak var deviceIdLabel: UILabel!
|
||||
|
||||
@IBOutlet weak var description1Label: UILabel!
|
||||
@IBOutlet weak var description2Label: UILabel!
|
||||
@IBOutlet weak var continueButtonBackgroundView: UIView!
|
||||
@IBOutlet weak var continueButton: UIButton!
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: DeviceVerificationIncomingViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: DeviceVerificationIncomingViewModelType) -> DeviceVerificationIncomingViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationIncomingViewController.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.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
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)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
}
|
||||
|
||||
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.description1Label.textColor = theme.textPrimaryColor
|
||||
self.description2Label.textColor = theme.textPrimaryColor
|
||||
|
||||
self.continueButton.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.continueButton)
|
||||
}
|
||||
|
||||
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.deviceVerificationIncomingTitle
|
||||
self.description1Label.text = VectorL10n.deviceVerificationIncomingDescription1
|
||||
self.description2Label.text = VectorL10n.deviceVerificationIncomingDescription2
|
||||
self.continueButton.setTitle(VectorL10n.continue, for: .normal)
|
||||
|
||||
if let avatarImageView = self.avatarImageView {
|
||||
let defaultavatarImage = AvatarGenerator.generateAvatar(forMatrixItem: self.viewModel.userId, withDisplayName: self.viewModel.userDisplayName)
|
||||
|
||||
avatarImageView.enableInMemoryCache = true
|
||||
avatarImageView.setImageURI(self.viewModel.avatarUrl, withType: nil, andImageOrientation: .up, previewImage: defaultavatarImage, mediaManager: self.viewModel.mediaManager)
|
||||
|
||||
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width / 2
|
||||
avatarImageView.clipsToBounds = true
|
||||
}
|
||||
|
||||
self.userDisplaynameLabel.text = self.viewModel.userDisplayName ?? self.viewModel.userId
|
||||
self.deviceIdLabel.text = self.viewModel.deviceId
|
||||
}
|
||||
|
||||
private func render(viewState: DeviceVerificationIncomingViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded:
|
||||
self.renderAccepted()
|
||||
case .cancelled(let reason):
|
||||
self.renderCancelled(reason: reason)
|
||||
case .cancelledByMe(let reason):
|
||||
self.renderCancelledByMe(reason: reason)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderAccepted() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
}
|
||||
|
||||
private func renderCancelled(reason: MXTransactionCancelCode) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderCancelledByMe(reason: MXTransactionCancelCode) {
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable), animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func continueButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .accept)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DeviceVerificationIncomingViewModelViewDelegate
|
||||
extension DeviceVerificationIncomingViewController: DeviceVerificationIncomingViewModelViewDelegate {
|
||||
|
||||
func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didUpdateViewState viewSate: DeviceVerificationIncomingViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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 DeviceVerificationIncomingViewModel: DeviceVerificationIncomingViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let transaction: MXIncomingSASTransaction
|
||||
|
||||
// MARK: Public
|
||||
|
||||
let userId: String
|
||||
let userDisplayName: String?
|
||||
let avatarUrl: String?
|
||||
let deviceId: String
|
||||
|
||||
let mediaManager: MXMediaManager
|
||||
|
||||
weak var viewDelegate: DeviceVerificationIncomingViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: DeviceVerificationIncomingViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUser: MXUser, transaction: MXIncomingSASTransaction) {
|
||||
self.session = session
|
||||
self.transaction = transaction
|
||||
self.userId = otherUser.userId
|
||||
self.userDisplayName = otherUser.displayname
|
||||
self.avatarUrl = otherUser.avatarUrl
|
||||
self.deviceId = transaction.otherDeviceId
|
||||
|
||||
self.mediaManager = session.mediaManager
|
||||
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: DeviceVerificationIncomingViewAction) {
|
||||
switch viewAction {
|
||||
case .accept:
|
||||
self.acceptIncomingDeviceVerification()
|
||||
case .cancel:
|
||||
self.rejectIncomingDeviceVerification()
|
||||
self.coordinatorDelegate?.deviceVerificationIncomingViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func acceptIncomingDeviceVerification() {
|
||||
self.update(viewState: .loading)
|
||||
self.transaction.accept()
|
||||
}
|
||||
|
||||
private func rejectIncomingDeviceVerification() {
|
||||
self.transaction.cancel(with: MXTransactionCancelCode.user())
|
||||
}
|
||||
|
||||
private func update(viewState: DeviceVerificationIncomingViewState) {
|
||||
self.viewDelegate?.deviceVerificationIncomingViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
// MARK: - MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification(transaction: MXIncomingSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXIncomingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.update(viewState: .loaded)
|
||||
self.coordinatorDelegate?.deviceVerificationIncomingViewModel(self, didAcceptTransaction: self.transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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 DeviceVerificationIncomingViewModelViewDelegate: class {
|
||||
func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didUpdateViewState viewSate: DeviceVerificationIncomingViewState)
|
||||
}
|
||||
|
||||
protocol DeviceVerificationIncomingViewModelCoordinatorDelegate: class {
|
||||
func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didAcceptTransaction transaction: MXSASTransaction)
|
||||
func deviceVerificationIncomingViewModelDidCancel(_ viewModel: DeviceVerificationIncomingViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `DeviceVerificationIncomingViewController`
|
||||
protocol DeviceVerificationIncomingViewModelType {
|
||||
|
||||
var userId: String { get }
|
||||
var userDisplayName: String? { get }
|
||||
var avatarUrl: String? { get }
|
||||
var deviceId: String { get }
|
||||
|
||||
var mediaManager: MXMediaManager { get }
|
||||
|
||||
var viewDelegate: DeviceVerificationIncomingViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: DeviceVerificationIncomingViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: DeviceVerificationIncomingViewAction)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Incoming DeviceVerificationIncoming
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationIncomingViewController view state
|
||||
enum DeviceVerificationIncomingViewState {
|
||||
case loading
|
||||
case loaded // accepted
|
||||
case cancelled(MXTransactionCancelCode)
|
||||
case cancelledByMe(MXTransactionCancelCode)
|
||||
case error(Error)
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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 DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoadingCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var deviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadingViewModelType
|
||||
private let deviceVerificationDataLoadingViewController: DeviceVerificationDataLoadingViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: DeviceVerificationDataLoadingCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUserId: String, otherDeviceId: String) {
|
||||
self.session = session
|
||||
|
||||
let deviceVerificationDataLoadingViewModel = DeviceVerificationDataLoadingViewModel(session: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
let deviceVerificationDataLoadingViewController = DeviceVerificationDataLoadingViewController.instantiate(with: deviceVerificationDataLoadingViewModel)
|
||||
self.deviceVerificationDataLoadingViewModel = deviceVerificationDataLoadingViewModel
|
||||
self.deviceVerificationDataLoadingViewController = deviceVerificationDataLoadingViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.deviceVerificationDataLoadingViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.deviceVerificationDataLoadingViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationDataLoadingViewModelCoordinatorDelegate
|
||||
extension DeviceVerificationDataLoadingCoordinator: DeviceVerificationDataLoadingViewModelCoordinatorDelegate {
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didLoadUser user: MXUser, device: MXDeviceInfo) {
|
||||
self.delegate?.deviceVerificationDataLoadingCoordinator(self, didLoadUser: user, device: device)
|
||||
}
|
||||
|
||||
func deviceVerificationDataLoadingViewModelDidCancel(_ viewModel: DeviceVerificationDataLoadingViewModelType) {
|
||||
self.delegate?.deviceVerificationDataLoadingCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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 DeviceVerificationDataLoadingCoordinatorDelegate: class {
|
||||
func deviceVerificationDataLoadingCoordinator(_ coordinator: DeviceVerificationDataLoadingCoordinatorType, didLoadUser user: MXUser, device: MXDeviceInfo)
|
||||
func deviceVerificationDataLoadingCoordinatorDidCancel(_ coordinator: DeviceVerificationDataLoadingCoordinatorType)
|
||||
}
|
||||
|
||||
/// `DeviceVerificationDataLoadingCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol DeviceVerificationDataLoadingCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: DeviceVerificationDataLoadingCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationDataLoadingViewController view actions exposed to view model
|
||||
enum DeviceVerificationDataLoadingViewAction {
|
||||
case cancel
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dBQ-CG-VDL">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Device Verification Data Loading View Controller-->
|
||||
<scene sceneID="EyC-m5-6uM">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="dBQ-CG-VDL" customClass="DeviceVerificationDataLoadingViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ht4-fu-3rS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jOh-c7-uod">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c4q-B8-hPy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="428"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fNE-v3-2lx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="428"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="9am-iX-rzi"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="top" secondItem="c4q-B8-hPy" secondAttribute="top" id="bHO-0I-Jjh"/>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="centerX" secondItem="c4q-B8-hPy" secondAttribute="centerX" id="fGs-s5-GHA"/>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="c4q-B8-hPy" secondAttribute="leading" id="jpJ-bp-Vmz"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fNE-v3-2lx" secondAttribute="trailing" id="juO-Zk-MPs"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fNE-v3-2lx" secondAttribute="bottom" id="sZa-ea-aZQ"/>
|
||||
<constraint firstAttribute="height" constant="428" id="vx4-4u-WS1"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="c4q-B8-hPy" secondAttribute="bottom" id="KlD-dP-EYo"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="width" secondItem="jOh-c7-uod" secondAttribute="width" id="Tly-og-biF"/>
|
||||
<constraint firstAttribute="trailing" secondItem="c4q-B8-hPy" secondAttribute="trailing" id="fNe-8B-X6c"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="leading" secondItem="jOh-c7-uod" secondAttribute="leading" id="h5p-NS-unN"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="top" secondItem="jOh-c7-uod" secondAttribute="top" id="zPm-BG-Pm8"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="jOh-c7-uod" secondAttribute="trailing" id="7K8-MG-xLT"/>
|
||||
<constraint firstItem="6ex-OQ-2sZ" firstAttribute="bottom" secondItem="jOh-c7-uod" secondAttribute="bottom" id="DGP-MJ-g6l"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="leading" secondItem="Ht4-fu-3rS" secondAttribute="leading" id="TGc-b5-uMu"/>
|
||||
<constraint firstItem="6ex-OQ-2sZ" firstAttribute="leading" secondItem="jOh-c7-uod" secondAttribute="leading" id="Z7r-yd-J4e"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="trailing" secondItem="6ex-OQ-2sZ" secondAttribute="trailing" id="jVN-Fr-MKN"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="top" secondItem="6ex-OQ-2sZ" secondAttribute="top" id="s7K-jf-P1z"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6ex-OQ-2sZ"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bLY-II-iJ3" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1703" y="255"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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 DeviceVerificationDataLoadingViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: DeviceVerificationDataLoadingViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: DeviceVerificationDataLoadingViewModelType) -> DeviceVerificationDataLoadingViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationDataLoadingViewController.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.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
|
||||
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 update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
}
|
||||
|
||||
private func render(viewState: DeviceVerificationDataLoadingViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded:
|
||||
self.renderLoaded()
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
case .errorMessage(let message):
|
||||
self.renderError(message: message)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
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: {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
})
|
||||
}
|
||||
|
||||
private func renderError(message: String) {
|
||||
self.errorPresenter.presentError(from: self, title: "", message: message, animated: true, handler: {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DeviceVerificationDataLoadingViewModelViewDelegate
|
||||
extension DeviceVerificationDataLoadingViewController: DeviceVerificationDataLoadingViewModelViewDelegate {
|
||||
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didUpdateViewState viewSate: DeviceVerificationDataLoadingViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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 DeviceVerificationDataLoadingViewModel: DeviceVerificationDataLoadingViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let otherUserId: String
|
||||
private let otherDeviceId: String
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: DeviceVerificationDataLoadingViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: DeviceVerificationDataLoadingViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUserId: String, otherDeviceId: String) {
|
||||
self.session = session
|
||||
self.otherUserId = otherUserId
|
||||
self.otherDeviceId = otherDeviceId
|
||||
|
||||
self.loadData()
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: DeviceVerificationDataLoadingViewAction) {
|
||||
switch viewAction {
|
||||
case .cancel:
|
||||
self.coordinatorDelegate?.deviceVerificationDataLoadingViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
if let otherUser = self.session.user(withUserId: otherUserId) {
|
||||
self.session.crypto?.downloadKeys([self.otherUserId], forceDownload: false, success: { [weak self] (usersDevicesMap) in
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
|
||||
sself.update(viewState: .loaded)
|
||||
|
||||
if let otherDevice = usersDevicesMap?.object(forDevice: sself.otherDeviceId, forUser: sself.otherUserId) {
|
||||
sself.coordinatorDelegate?.deviceVerificationDataLoadingViewModel(sself, didLoadUser: otherUser, device: otherDevice)
|
||||
} else {
|
||||
sself.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
}
|
||||
|
||||
}, failure: { [weak self] (error) in
|
||||
guard let sself = self, let error = error else {
|
||||
return
|
||||
}
|
||||
|
||||
sself.update(viewState: .error(error))
|
||||
})
|
||||
|
||||
} else {
|
||||
self.update(viewState: .errorMessage(VectorL10n.deviceVerificationErrorCannotLoadDevice))
|
||||
}
|
||||
}
|
||||
|
||||
private func update(viewState: DeviceVerificationDataLoadingViewState) {
|
||||
self.viewDelegate?.deviceVerificationDataLoadingViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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 DeviceVerificationDataLoadingViewModelViewDelegate: class {
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didUpdateViewState viewSate: DeviceVerificationDataLoadingViewState)
|
||||
}
|
||||
|
||||
protocol DeviceVerificationDataLoadingViewModelCoordinatorDelegate: class {
|
||||
func deviceVerificationDataLoadingViewModel(_ viewModel: DeviceVerificationDataLoadingViewModelType, didLoadUser user: MXUser, device: MXDeviceInfo)
|
||||
func deviceVerificationDataLoadingViewModelDidCancel(_ viewModel: DeviceVerificationDataLoadingViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `DeviceVerificationDataLoadingViewController`
|
||||
protocol DeviceVerificationDataLoadingViewModelType {
|
||||
|
||||
var viewDelegate: DeviceVerificationDataLoadingViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: DeviceVerificationDataLoadingViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: DeviceVerificationDataLoadingViewAction)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Loading DeviceVerificationDataLoading
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationDataLoadingViewController view state
|
||||
enum DeviceVerificationDataLoadingViewState {
|
||||
case loading
|
||||
case loaded
|
||||
case error(Error)
|
||||
case errorMessage(String)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationStartCoordinator: DeviceVerificationStartCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var deviceVerificationStartViewModel: DeviceVerificationStartViewModelType
|
||||
private let deviceVerificationStartViewController: DeviceVerificationStartViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: DeviceVerificationStartCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUser: MXUser, otherDevice: MXDeviceInfo) {
|
||||
self.session = session
|
||||
|
||||
let deviceVerificationStartViewModel = DeviceVerificationStartViewModel(session: self.session, otherUser: otherUser, otherDevice: otherDevice)
|
||||
let deviceVerificationStartViewController = DeviceVerificationStartViewController.instantiate(with: deviceVerificationStartViewModel)
|
||||
self.deviceVerificationStartViewModel = deviceVerificationStartViewModel
|
||||
self.deviceVerificationStartViewController = deviceVerificationStartViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.deviceVerificationStartViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.deviceVerificationStartViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationStartViewModelCoordinatorDelegate
|
||||
extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCoordinatorDelegate {
|
||||
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) {
|
||||
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.deviceVerificationStartCoordinator(self, didTransactionCancelled: transaction)
|
||||
}
|
||||
|
||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) {
|
||||
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationStartCoordinatorDelegate: class {
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType)
|
||||
}
|
||||
|
||||
/// `DeviceVerificationStartCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol DeviceVerificationStartCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: DeviceVerificationStartCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationStartViewController view actions exposed to view model
|
||||
enum DeviceVerificationStartViewAction {
|
||||
case beginVerifying
|
||||
case verifyUsingLegacy
|
||||
case verifiedUsingLegacy
|
||||
case cancel
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Device Verification Start View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="DeviceVerificationStartViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="494"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="494"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verify by comparing a short text string" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="he8-pl-xE9">
|
||||
<rect key="frame" x="20" y="35" width="335" height="41"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="41" id="Nam-ca-50k"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For maximum security, we recommend you do this in person or use another trusted means of communication." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
|
||||
<rect key="frame" x="20" y="86" width="335" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="80" id="Y2u-fJ-idS"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xlN-AB-1V0">
|
||||
<rect key="frame" x="20" y="316" width="335" height="100"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="100" id="Pes-rZ-lpS"/>
|
||||
<constraint firstAttribute="width" constant="335" id="caQ-aT-Sw4"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DtR-jx-UKY">
|
||||
<rect key="frame" x="0.0" y="366" width="375" height="50"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
|
||||
<rect key="frame" x="20" y="10" width="335" height="30"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<state key="normal" title="Begin Verifying">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="verifyButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerY" secondItem="DtR-jx-UKY" secondAttribute="centerY" id="5eX-a5-zpP"/>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerX" secondItem="DtR-jx-UKY" secondAttribute="centerX" id="6v9-MN-mk2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DOt-5E-FjF" secondAttribute="trailing" constant="20" id="7Cr-6b-kGn"/>
|
||||
<constraint firstAttribute="height" constant="50" id="QNq-au-ZdL"/>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="leading" secondItem="DtR-jx-UKY" secondAttribute="leading" constant="20" id="YrM-nf-AuH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EBO-vX-zj4">
|
||||
<rect key="frame" x="20" y="424" width="335" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="oyt-F3-W0L"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Use Legacy Verification">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="useLegacyVerificationButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="7j6-Xn-y8s"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for partner to accept..." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6yX-xD-4X5">
|
||||
<rect key="frame" x="20" y="213" width="335" height="54"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="335" id="9C0-ev-AVw"/>
|
||||
<constraint firstAttribute="height" constant="54" id="pta-eP-0yH"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="EBO-vX-zj4" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="4d3-TM-5jh"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="47" id="6Bh-e4-HVr"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="bottom" secondItem="xlN-AB-1V0" secondAttribute="bottom" id="Chw-nj-X21"/>
|
||||
<constraint firstAttribute="trailing" secondItem="EBO-vX-zj4" secondAttribute="trailing" constant="20" id="HGR-53-jN9"/>
|
||||
<constraint firstItem="xlN-AB-1V0" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="K7S-YK-nPU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DtR-jx-UKY" secondAttribute="trailing" id="K7y-Df-dgz"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="bxI-mu-qng" secondAttribute="leading" id="Q9n-7p-gHl"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="QSg-yz-aaB"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="Rg4-jV-Nht"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="200" id="Smz-GL-JdW"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ZP8-mV-RBh"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" id="Zeg-U8-uis"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="375" id="glD-Sz-73O"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="hK0-aA-d9H"/>
|
||||
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="top" constant="51" id="oX9-J1-Y9r"/>
|
||||
<constraint firstAttribute="bottom" secondItem="EBO-vX-zj4" secondAttribute="bottom" constant="20" id="rbj-JL-Oyi"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="35" id="s3k-Io-834"/>
|
||||
<constraint firstItem="EBO-vX-zj4" firstAttribute="top" secondItem="xlN-AB-1V0" secondAttribute="bottom" constant="8" id="wRB-mm-fD5"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" constant="70" id="Y46-NP-zAc"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="informationLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
|
||||
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
|
||||
<outlet property="titleLabel" destination="he8-pl-xE9" id="btA-kv-E2B"/>
|
||||
<outlet property="useLegacyVerificationButton" destination="EBO-vX-zj4" id="V9h-0k-ExA"/>
|
||||
<outlet property="useLegacyVerificationLabel" destination="xlN-AB-1V0" id="k33-JB-Jnc"/>
|
||||
<outlet property="verifyButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
|
||||
<outlet property="verifyButtonBackgroundView" destination="DtR-jx-UKY" id="9yG-wP-u8A"/>
|
||||
<outlet property="waitingPartnerLabel" destination="6yX-xD-4X5" id="fre-bc-Kma"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3199.1999999999998" y="-647.22638680659679"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,250 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationStartViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// 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 waitingPartnerLabel: UILabel!
|
||||
@IBOutlet private weak var useLegacyVerificationLabel: UILabel!
|
||||
@IBOutlet private weak var verifyButtonBackgroundView: UIView!
|
||||
@IBOutlet private weak var verifyButton: UIButton!
|
||||
@IBOutlet private weak var useLegacyVerificationButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: DeviceVerificationStartViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: DeviceVerificationStartViewModelType) -> DeviceVerificationStartViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationStartViewController.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.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
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)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
}
|
||||
|
||||
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.waitingPartnerLabel.textColor = theme.textPrimaryColor
|
||||
self.useLegacyVerificationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.verifyButton.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.verifyButton)
|
||||
|
||||
theme.applyStyle(onButton: self.useLegacyVerificationButton)
|
||||
}
|
||||
|
||||
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.deviceVerificationStartTitle
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice
|
||||
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationStartWaitPartner
|
||||
self.useLegacyVerificationLabel.text = VectorL10n.deviceVerificationStartUseLegacy
|
||||
|
||||
self.waitingPartnerLabel.isHidden = true
|
||||
self.useLegacyVerificationLabel.isHidden = true
|
||||
|
||||
self.verifyButton.setTitle(VectorL10n.deviceVerificationStartVerifyButton, for: .normal)
|
||||
self.useLegacyVerificationButton.setTitle(VectorL10n.deviceVerificationStartUseLegacyAction, for: .normal)
|
||||
}
|
||||
|
||||
private func render(viewState: DeviceVerificationStartViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded:
|
||||
self.renderStarted()
|
||||
case .verifyUsingLegacy(let session, let deviceInfo):
|
||||
self.renderVerifyUsingLegacy(session: session, deviceInfo: deviceInfo)
|
||||
case .cancelled(let reason):
|
||||
self.renderCancelled(reason: reason)
|
||||
case .cancelledByMe(let reason):
|
||||
self.renderCancelledByMe(reason: reason)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderStarted() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.verifyButtonBackgroundView.isHidden = true
|
||||
self.waitingPartnerLabel.isHidden = false
|
||||
self.useLegacyVerificationLabel.isHidden = false
|
||||
}
|
||||
|
||||
private func renderVerifyUsingLegacy(session: MXSession, deviceInfo: MXDeviceInfo) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
guard let encryptionInfoView = EncryptionInfoView(deviceInfo: deviceInfo, andMatrixSession: session) else {
|
||||
return
|
||||
}
|
||||
|
||||
encryptionInfoView.delegate = self
|
||||
|
||||
// Skip the intro page
|
||||
encryptionInfoView.displayLegacyVerificationScreen()
|
||||
|
||||
// Display the legacy verification view in full screen
|
||||
// TODO: Do not reuse the legacy EncryptionInfoView and create a screen from scratch
|
||||
self.view.vc_addSubViewMatchingParent(encryptionInfoView)
|
||||
self.navigationController?.isNavigationBarHidden = true
|
||||
}
|
||||
|
||||
private func renderCancelled(reason: MXTransactionCancelCode) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderCancelledByMe(reason: MXTransactionCancelCode) {
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable), animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func verifyButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .beginVerifying)
|
||||
}
|
||||
|
||||
@IBAction private func useLegacyVerificationButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .verifyUsingLegacy)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DeviceVerificationStartViewModelViewDelegate
|
||||
extension DeviceVerificationStartViewController: DeviceVerificationStartViewModelViewDelegate {
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didUpdateViewState viewSate: DeviceVerificationStartViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationStartViewModelViewDelegate
|
||||
extension DeviceVerificationStartViewController: MXKEncryptionInfoViewDelegate {
|
||||
func encryptionInfoView(_ encryptionInfoView: MXKEncryptionInfoView!, didDeviceInfoVerifiedChange deviceInfo: MXDeviceInfo!) {
|
||||
self.viewModel.process(viewAction: .verifiedUsingLegacy)
|
||||
}
|
||||
|
||||
func encryptionInfoViewDidClose(_ encryptionInfoView: MXKEncryptionInfoView!) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationStartViewModel: DeviceVerificationStartViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let verificationManager: MXDeviceVerificationManager
|
||||
private let otherUser: MXUser
|
||||
private let otherDevice: MXDeviceInfo
|
||||
|
||||
private var transaction: MXSASTransaction!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: DeviceVerificationStartViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, otherUser: MXUser, otherDevice: MXDeviceInfo) {
|
||||
self.session = session
|
||||
self.verificationManager = session.crypto.deviceVerificationManager
|
||||
self.otherUser = otherUser
|
||||
self.otherDevice = otherDevice
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: DeviceVerificationStartViewAction) {
|
||||
switch viewAction {
|
||||
case .beginVerifying:
|
||||
self.beginVerifying()
|
||||
case .verifyUsingLegacy:
|
||||
self.cancelTransaction()
|
||||
self.update(viewState: .verifyUsingLegacy(self.session, self.otherDevice))
|
||||
case .verifiedUsingLegacy:
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidUseLegacyVerification(self)
|
||||
case .cancel:
|
||||
self.cancelTransaction()
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func beginVerifying() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: MXKeyVerificationMethodSAS, success: { [weak self] (transaction) in
|
||||
|
||||
guard let sself = self else {
|
||||
return
|
||||
}
|
||||
guard let sasTransaction: MXOutgoingSASTransaction = transaction as? MXOutgoingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
sself.transaction = sasTransaction
|
||||
|
||||
sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction)
|
||||
sself.update(viewState: .loaded)
|
||||
}, failure: {[weak self] error in
|
||||
self?.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func cancelTransaction() {
|
||||
guard let transaction = self.transaction else {
|
||||
return
|
||||
}
|
||||
|
||||
transaction.cancel(with: MXTransactionCancelCode.user())
|
||||
}
|
||||
|
||||
private func update(viewState: DeviceVerificationStartViewState) {
|
||||
self.viewDelegate?.deviceVerificationStartViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification(transaction: MXOutgoingSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXOutgoingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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 DeviceVerificationStartViewModelViewDelegate: class {
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didUpdateViewState viewSate: DeviceVerificationStartViewState)
|
||||
}
|
||||
|
||||
protocol DeviceVerificationStartViewModelCoordinatorDelegate: class {
|
||||
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType)
|
||||
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `DeviceVerificationStartViewController`
|
||||
protocol DeviceVerificationStartViewModelType {
|
||||
var viewDelegate: DeviceVerificationStartViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: DeviceVerificationStartViewAction)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationStartViewController view state
|
||||
enum DeviceVerificationStartViewState {
|
||||
case loading
|
||||
case loaded // started
|
||||
case verifyUsingLegacy(MXSession, MXDeviceInfo)
|
||||
case cancelled(MXTransactionCancelCode)
|
||||
case cancelledByMe(MXTransactionCancelCode)
|
||||
case error(Error)
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dBQ-CG-VDL">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Device Verification Verified View Controller-->
|
||||
<scene sceneID="EyC-m5-6uM">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="dBQ-CG-VDL" customClass="DeviceVerificationVerifiedViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ht4-fu-3rS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jOh-c7-uod">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c4q-B8-hPy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="432.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fNE-v3-2lx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="432.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verified!" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Nw-CZ-lKr">
|
||||
<rect key="frame" x="20" y="60" width="335" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="30" translatesAutoresizingMaskIntoConstraints="NO" id="12d-Dc-Rlv">
|
||||
<rect key="frame" x="20" y="160.5" width="335" height="102"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="You've successfully verified this device." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5dj-7z-eH5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="335" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Secure messages with this user are end-to-end encrypted and not able to be read by third parties." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="spd-ah-EYp">
|
||||
<rect key="frame" x="0.0" y="48" width="335" height="54"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="50" translatesAutoresizingMaskIntoConstraints="NO" id="4Ll-vk-JLe">
|
||||
<rect key="frame" x="0.0" y="362.5" width="375" height="50"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wbk-EX-kTs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kgv-EZ-dF9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="WcJ-IL-5KV"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
|
||||
<state key="normal" title="Got it">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="validateButtonAction:" destination="dBQ-CG-VDL" eventType="touchUpInside" id="kpR-g5-ogv"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kgv-EZ-dF9" firstAttribute="top" secondItem="Wbk-EX-kTs" secondAttribute="top" id="1mu-8N-etF"/>
|
||||
<constraint firstAttribute="bottom" secondItem="kgv-EZ-dF9" secondAttribute="bottom" id="2ce-b1-aV5"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kgv-EZ-dF9" secondAttribute="trailing" id="OHz-zo-Uvl"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="eud-Ba-XSx"/>
|
||||
<constraint firstItem="kgv-EZ-dF9" firstAttribute="leading" secondItem="Wbk-EX-kTs" secondAttribute="leading" id="gPb-HX-NWn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="12d-Dc-Rlv" firstAttribute="leading" secondItem="1Nw-CZ-lKr" secondAttribute="leading" id="5Kb-MI-4lP"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="9am-iX-rzi"/>
|
||||
<constraint firstAttribute="trailing" secondItem="4Ll-vk-JLe" secondAttribute="trailing" id="CU4-Sr-hLT"/>
|
||||
<constraint firstItem="12d-Dc-Rlv" firstAttribute="trailing" secondItem="1Nw-CZ-lKr" secondAttribute="trailing" id="Cax-X9-xf8"/>
|
||||
<constraint firstItem="4Ll-vk-JLe" firstAttribute="leading" secondItem="fNE-v3-2lx" secondAttribute="leading" id="FGu-8C-v1U"/>
|
||||
<constraint firstItem="4Ll-vk-JLe" firstAttribute="top" secondItem="12d-Dc-Rlv" secondAttribute="bottom" constant="100" id="Hue-GK-ORf"/>
|
||||
<constraint firstItem="1Nw-CZ-lKr" firstAttribute="leading" secondItem="fNE-v3-2lx" secondAttribute="leading" constant="20" id="LK5-9b-xDf"/>
|
||||
<constraint firstItem="1Nw-CZ-lKr" firstAttribute="top" secondItem="fNE-v3-2lx" secondAttribute="top" constant="60" id="MUK-4D-vke"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4Ll-vk-JLe" secondAttribute="bottom" constant="20" id="Vn1-zQ-G8t"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1Nw-CZ-lKr" secondAttribute="trailing" constant="20" id="WKJ-Ta-cVg"/>
|
||||
<constraint firstItem="12d-Dc-Rlv" firstAttribute="centerX" secondItem="fNE-v3-2lx" secondAttribute="centerX" id="ksz-nC-DeX"/>
|
||||
<constraint firstItem="12d-Dc-Rlv" firstAttribute="top" secondItem="1Nw-CZ-lKr" secondAttribute="bottom" constant="80" id="rhF-BF-2cR"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="top" secondItem="c4q-B8-hPy" secondAttribute="top" id="bHO-0I-Jjh"/>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="centerX" secondItem="c4q-B8-hPy" secondAttribute="centerX" id="fGs-s5-GHA"/>
|
||||
<constraint firstItem="fNE-v3-2lx" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="c4q-B8-hPy" secondAttribute="leading" id="jpJ-bp-Vmz"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fNE-v3-2lx" secondAttribute="trailing" id="juO-Zk-MPs"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fNE-v3-2lx" secondAttribute="bottom" id="sZa-ea-aZQ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="c4q-B8-hPy" secondAttribute="bottom" id="KlD-dP-EYo"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="width" secondItem="jOh-c7-uod" secondAttribute="width" id="Tly-og-biF"/>
|
||||
<constraint firstAttribute="trailing" secondItem="c4q-B8-hPy" secondAttribute="trailing" id="fNe-8B-X6c"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="leading" secondItem="jOh-c7-uod" secondAttribute="leading" id="h5p-NS-unN"/>
|
||||
<constraint firstItem="c4q-B8-hPy" firstAttribute="top" secondItem="jOh-c7-uod" secondAttribute="top" id="zPm-BG-Pm8"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="jOh-c7-uod" secondAttribute="trailing" id="7K8-MG-xLT"/>
|
||||
<constraint firstItem="6ex-OQ-2sZ" firstAttribute="bottom" secondItem="jOh-c7-uod" secondAttribute="bottom" id="DGP-MJ-g6l"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="leading" secondItem="Ht4-fu-3rS" secondAttribute="leading" id="TGc-b5-uMu"/>
|
||||
<constraint firstItem="6ex-OQ-2sZ" firstAttribute="leading" secondItem="jOh-c7-uod" secondAttribute="leading" id="Z7r-yd-J4e"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="trailing" secondItem="6ex-OQ-2sZ" secondAttribute="trailing" id="jVN-Fr-MKN"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="top" secondItem="6ex-OQ-2sZ" secondAttribute="top" id="s7K-jf-P1z"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6ex-OQ-2sZ"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="description1Label" destination="5dj-7z-eH5" id="y4w-i9-5hB"/>
|
||||
<outlet property="description2Label" destination="spd-ah-EYp" id="TXD-8P-gF0"/>
|
||||
<outlet property="okButton" destination="kgv-EZ-dF9" id="do0-Ot-OKn"/>
|
||||
<outlet property="okButtonBackgroundView" destination="Wbk-EX-kTs" id="2gv-gg-ROL"/>
|
||||
<outlet property="titleLabel" destination="1Nw-CZ-lKr" id="zXP-Xt-Zl9"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bLY-II-iJ3" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1703" y="255"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// File created from simpleScreenTemplate
|
||||
// $ createSimpleScreen.sh DeviceVerification/Verified DeviceVerificationVerified
|
||||
/*
|
||||
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 DeviceVerificationVerifiedViewControllerDelegate: class {
|
||||
func deviceVerificationVerifiedViewControllerDidTapSetupAction(_ viewController: DeviceVerificationVerifiedViewController)
|
||||
func deviceVerificationVerifiedViewControllerDidCancel(_ viewController: DeviceVerificationVerifiedViewController)
|
||||
}
|
||||
|
||||
final class DeviceVerificationVerifiedViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var description1Label: UILabel!
|
||||
@IBOutlet private weak var description2Label: UILabel!
|
||||
|
||||
@IBOutlet private weak var okButtonBackgroundView: UIView!
|
||||
@IBOutlet private weak var okButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: DeviceVerificationVerifiedViewControllerDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate() -> DeviceVerificationVerifiedViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationVerifiedViewController.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.deviceVerificationTitle
|
||||
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() {
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifiedTitle
|
||||
self.description1Label.text = VectorL10n.deviceVerificationVerifiedDescription1
|
||||
self.description2Label.text = VectorL10n.deviceVerificationVerifiedDescription2
|
||||
|
||||
self.okButton.setTitle(VectorL10n.deviceVerificationVerifiedGotItButton, 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.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.description1Label.textColor = theme.textPrimaryColor
|
||||
self.description2Label.textColor = theme.textPrimaryColor
|
||||
|
||||
self.okButtonBackgroundView.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.okButton)
|
||||
}
|
||||
|
||||
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 validateButtonAction(_ sender: Any) {
|
||||
self.delegate?.deviceVerificationVerifiedViewControllerDidTapSetupAction(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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 DeviceVerificationVerifyCoordinator: DeviceVerificationVerifyCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var deviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModelType
|
||||
private let deviceVerificationVerifyViewController: DeviceVerificationVerifyViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: DeviceVerificationVerifyCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, transaction: MXSASTransaction) {
|
||||
self.session = session
|
||||
|
||||
let deviceVerificationVerifyViewModel = DeviceVerificationVerifyViewModel(session: self.session, transaction: transaction)
|
||||
let deviceVerificationVerifyViewController = DeviceVerificationVerifyViewController.instantiate(with: deviceVerificationVerifyViewModel)
|
||||
self.deviceVerificationVerifyViewModel = deviceVerificationVerifyViewModel
|
||||
self.deviceVerificationVerifyViewController = deviceVerificationVerifyViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.deviceVerificationVerifyViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.deviceVerificationVerifyViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeviceVerificationVerifyViewModelCoordinatorDelegate
|
||||
extension DeviceVerificationVerifyCoordinator: DeviceVerificationVerifyViewModelCoordinatorDelegate {
|
||||
|
||||
func deviceVerificationVerifyViewModelDidComplete(_ viewModel: DeviceVerificationVerifyViewModelType) {
|
||||
self.delegate?.deviceVerificationVerifyCoordinatorDidComplete(self)
|
||||
}
|
||||
|
||||
func deviceVerificationVerifyViewModelDidCancel(_ viewModel: DeviceVerificationVerifyViewModelType) {
|
||||
self.delegate?.deviceVerificationVerifyCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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 DeviceVerificationVerifyCoordinatorDelegate: class {
|
||||
func deviceVerificationVerifyCoordinatorDidComplete(_ coordinator: DeviceVerificationVerifyCoordinatorType)
|
||||
func deviceVerificationVerifyCoordinatorDidCancel(_ coordinator: DeviceVerificationVerifyCoordinatorType)
|
||||
}
|
||||
|
||||
/// `DeviceVerificationVerifyCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol DeviceVerificationVerifyCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: DeviceVerificationVerifyCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationVerifyViewController view actions exposed to view model
|
||||
enum DeviceVerificationVerifyViewAction {
|
||||
case confirm
|
||||
case complete
|
||||
case cancel
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Device Verification Verify View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="DeviceVerificationVerifyViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verify this device by confirming the following emoji appear on the screen of the partner" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="he8-pl-xE9">
|
||||
<rect key="frame" x="20" y="35" width="335" height="61"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="61" id="Nam-ca-50k"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For maximum security, we recommend you do this in person or use another trusted means of communication." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
|
||||
<rect key="frame" x="20" y="115" width="335" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="80" id="3ag-pn-F2b"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3152 3307 8179" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RD6-ue-X5c">
|
||||
<rect key="frame" x="37.5" y="290.5" width="300" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="300" id="6Q5-M2-ifj"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DtR-jx-UKY">
|
||||
<rect key="frame" x="0.0" y="415" width="375" height="50"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
|
||||
<rect key="frame" x="156.5" y="10" width="62" height="30"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<state key="normal" title="Continue">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="continueButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerY" secondItem="DtR-jx-UKY" secondAttribute="centerY" id="5eX-a5-zpP"/>
|
||||
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerX" secondItem="DtR-jx-UKY" secondAttribute="centerX" id="6v9-MN-mk2"/>
|
||||
<constraint firstAttribute="height" constant="50" id="QNq-au-ZdL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for partner to confirm..." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6yX-xD-4X5">
|
||||
<rect key="frame" x="20" y="242" width="335" height="54"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="335" id="9C0-ev-AVw"/>
|
||||
<constraint firstAttribute="height" constant="54" id="pta-eP-0yH"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="34i-3X-YvQ">
|
||||
<rect key="frame" x="0.0" y="215" width="375" height="180"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="180" id="kpT-ty-CDI"/>
|
||||
</constraints>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="0.0" id="mHm-Tg-xbO">
|
||||
<size key="itemSize" width="80" height="80"/>
|
||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="VerifyEmojiCollectionViewCell" id="iG2-Pq-pYr" customClass="VerifyEmojiCollectionViewCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="🐶" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Cy-oI-To3">
|
||||
<rect key="frame" x="0.0" y="6" width="80" height="43"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="36"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="dog" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="axP-53-KGQ">
|
||||
<rect key="frame" x="0.0" y="58" width="80" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="5Cy-oI-To3" secondAttribute="trailing" id="0vM-mM-9UI"/>
|
||||
<constraint firstItem="axP-53-KGQ" firstAttribute="leading" secondItem="iG2-Pq-pYr" secondAttribute="leading" id="LeM-oa-STt"/>
|
||||
<constraint firstItem="5Cy-oI-To3" firstAttribute="top" secondItem="iG2-Pq-pYr" secondAttribute="top" constant="6" id="ZBA-TS-ThT"/>
|
||||
<constraint firstItem="5Cy-oI-To3" firstAttribute="leading" secondItem="iG2-Pq-pYr" secondAttribute="leading" id="hZz-hI-6k2"/>
|
||||
<constraint firstAttribute="bottom" secondItem="axP-53-KGQ" secondAttribute="bottom" constant="6" id="ja0-Lm-Kej"/>
|
||||
<constraint firstAttribute="trailing" secondItem="axP-53-KGQ" secondAttribute="trailing" id="yur-4a-SMc"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="emoji" destination="5Cy-oI-To3" id="pOP-P0-x8a"/>
|
||||
<outlet property="name" destination="axP-53-KGQ" id="JYh-RY-edf"/>
|
||||
</connections>
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="V8j-Lb-PgC" id="3m4-oH-KIj"/>
|
||||
</connections>
|
||||
</collectionView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="47" id="6Bh-e4-HVr"/>
|
||||
<constraint firstItem="34i-3X-YvQ" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="Bb1-EE-rq0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="DtR-jx-UKY" secondAttribute="bottom" constant="20" id="Cnb-UW-hYt"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DtR-jx-UKY" secondAttribute="trailing" id="K7y-Df-dgz"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="bxI-mu-qng" secondAttribute="leading" id="Q9n-7p-gHl"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="QSg-yz-aaB"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="Rg4-jV-Nht"/>
|
||||
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="top" constant="80" id="UL4-x0-oFT"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ZP8-mV-RBh"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" id="Zeg-U8-uis"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="375" id="glD-Sz-73O"/>
|
||||
<constraint firstItem="RD6-ue-X5c" firstAttribute="centerY" secondItem="34i-3X-YvQ" secondAttribute="centerY" id="h8F-gc-do4"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="hK0-aA-d9H"/>
|
||||
<constraint firstAttribute="trailing" secondItem="34i-3X-YvQ" secondAttribute="trailing" id="hg6-kq-M6U"/>
|
||||
<constraint firstItem="RD6-ue-X5c" firstAttribute="centerX" secondItem="34i-3X-YvQ" secondAttribute="centerX" id="lVE-w6-pIa"/>
|
||||
<constraint firstItem="DtR-jx-UKY" firstAttribute="top" secondItem="34i-3X-YvQ" secondAttribute="bottom" constant="20" id="liF-Qn-tiw"/>
|
||||
<constraint firstItem="34i-3X-YvQ" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="20" id="r7A-9g-Mmb"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="35" id="s3k-Io-834"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="subviews">
|
||||
<exclude reference="6yX-xD-4X5"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" constant="70" id="Y46-NP-zAc"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="continueButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
|
||||
<outlet property="continueButtonBackgroundView" destination="DtR-jx-UKY" id="9yG-wP-u8A"/>
|
||||
<outlet property="decimalLabel" destination="RD6-ue-X5c" id="wlK-nl-CB6"/>
|
||||
<outlet property="emojisCollectionView" destination="34i-3X-YvQ" id="wDE-oG-peo"/>
|
||||
<outlet property="informationLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
|
||||
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
|
||||
<outlet property="titleLabel" destination="he8-pl-xE9" id="btA-kv-E2B"/>
|
||||
<outlet property="waitingPartnerLabel" destination="6yX-xD-4X5" id="fre-bc-Kma"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3199.1999999999998" y="-647.22638680659679"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,251 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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 DeviceVerificationVerifyViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// 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 decimalLabel: UILabel!
|
||||
@IBOutlet private weak var emojisCollectionView: UICollectionView!
|
||||
@IBOutlet private weak var waitingPartnerLabel: UILabel!
|
||||
@IBOutlet private weak var continueButtonBackgroundView: UIView!
|
||||
@IBOutlet private weak var continueButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: DeviceVerificationVerifyViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: DeviceVerificationVerifyViewModelType) -> DeviceVerificationVerifyViewController {
|
||||
let viewController = StoryboardScene.DeviceVerificationVerifyViewController.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.deviceVerificationTitle
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Hide back button
|
||||
self.navigationItem.setHidesBackButton(true, animated: animated)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
}
|
||||
|
||||
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.decimalLabel.textColor = theme.textPrimaryColor
|
||||
self.waitingPartnerLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.continueButton.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.continueButton)
|
||||
|
||||
emojisCollectionView.reloadData()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if viewModel.emojis != nil {
|
||||
self.decimalLabel.isHidden = true
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifyTitleEmoji
|
||||
} else {
|
||||
self.emojisCollectionView.isHidden = true
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationVerifyTitleNumber
|
||||
self.decimalLabel.text = self.viewModel.decimal
|
||||
}
|
||||
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice
|
||||
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner
|
||||
|
||||
self.waitingPartnerLabel.isHidden = true
|
||||
|
||||
self.continueButton.setTitle(VectorL10n.continue, for: .normal)
|
||||
}
|
||||
|
||||
private func render(viewState: DeviceVerificationVerifyViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded:
|
||||
self.renderVerified()
|
||||
case .cancelled(let reason):
|
||||
self.renderCancelled(reason: reason)
|
||||
case .cancelledByMe(let reason):
|
||||
self.renderCancelledByMe(reason: reason)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderVerified() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.continueButtonBackgroundView.isHidden = true
|
||||
self.waitingPartnerLabel.isHidden = false
|
||||
}
|
||||
|
||||
private func renderCancelled(reason: MXTransactionCancelCode) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderCancelledByMe(reason: MXTransactionCancelCode) {
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable), animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func continueButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .confirm)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DeviceVerificationVerifyViewModelViewDelegate
|
||||
extension DeviceVerificationVerifyViewController: DeviceVerificationVerifyViewModelViewDelegate {
|
||||
|
||||
func deviceVerificationVerifyViewModel(_ viewModel: DeviceVerificationVerifyViewModelType, didUpdateViewState viewSate: DeviceVerificationVerifyViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension DeviceVerificationVerifyViewController: UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
guard let emojis = self.viewModel.emojis else {
|
||||
return 0
|
||||
}
|
||||
return emojis.count
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
|
||||
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VerifyEmojiCollectionViewCell", for: indexPath) as? VerifyEmojiCollectionViewCell else {
|
||||
return UICollectionViewCell()
|
||||
}
|
||||
|
||||
guard let emoji = self.viewModel.emojis?[indexPath.row] else {
|
||||
return UICollectionViewCell()
|
||||
}
|
||||
|
||||
cell.emoji.text = emoji.emoji
|
||||
cell.name.text = VectorL10n.tr("Vector", "device_verification_emoji_\(emoji.name)")
|
||||
|
||||
cell.update(theme: self.theme)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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 DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let transaction: MXSASTransaction
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: DeviceVerificationVerifyViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: DeviceVerificationVerifyViewModelCoordinatorDelegate?
|
||||
var emojis: [MXEmojiRepresentation]?
|
||||
var decimal: String?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, transaction: MXSASTransaction) {
|
||||
self.session = session
|
||||
self.transaction = transaction
|
||||
self.emojis = self.transaction.sasEmoji
|
||||
self.decimal = self.transaction.sasDecimal
|
||||
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: DeviceVerificationVerifyViewAction) {
|
||||
switch viewAction {
|
||||
case .confirm:
|
||||
self.confirmTransaction()
|
||||
case .complete:
|
||||
self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidComplete(self)
|
||||
case .cancel:
|
||||
self.cancelTransaction()
|
||||
self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func confirmTransaction() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.transaction.confirmSASMatch()
|
||||
}
|
||||
|
||||
private func cancelTransaction() {
|
||||
self.transaction.cancel(with: MXTransactionCancelCode.user())
|
||||
}
|
||||
|
||||
private func update(viewState: DeviceVerificationVerifyViewState) {
|
||||
self.viewDelegate?.deviceVerificationVerifyViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
// MARK: - MXDeviceVerificationTransactionDidChange
|
||||
|
||||
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateVerified:
|
||||
self.update(viewState: .loaded)
|
||||
self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidComplete(self)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateError:
|
||||
guard let error = transaction.error else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .error(error))
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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 DeviceVerificationVerifyViewModelViewDelegate: class {
|
||||
func deviceVerificationVerifyViewModel(_ viewModel: DeviceVerificationVerifyViewModelType, didUpdateViewState viewSate: DeviceVerificationVerifyViewState)
|
||||
}
|
||||
|
||||
protocol DeviceVerificationVerifyViewModelCoordinatorDelegate: class {
|
||||
func deviceVerificationVerifyViewModelDidComplete(_ viewModel: DeviceVerificationVerifyViewModelType)
|
||||
func deviceVerificationVerifyViewModelDidCancel(_ viewModel: DeviceVerificationVerifyViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `DeviceVerificationVerifyViewController`
|
||||
protocol DeviceVerificationVerifyViewModelType {
|
||||
|
||||
var viewDelegate: DeviceVerificationVerifyViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: DeviceVerificationVerifyViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: DeviceVerificationVerifyViewAction)
|
||||
|
||||
var emojis: [MXEmojiRepresentation]? { get set }
|
||||
var decimal: String? { get set }
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
|
||||
/*
|
||||
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
|
||||
|
||||
/// DeviceVerificationVerifyViewController view state
|
||||
enum DeviceVerificationVerifyViewState {
|
||||
case loading
|
||||
case loaded // verified
|
||||
case cancelled(MXTransactionCancelCode)
|
||||
case cancelledByMe(MXTransactionCancelCode)
|
||||
case error(Error)
|
||||
}
|
||||
@@ -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 UIKit
|
||||
|
||||
class VerifyEmojiCollectionViewCell: UICollectionViewCell, Themable {
|
||||
@IBOutlet weak var emoji: UILabel!
|
||||
@IBOutlet weak var name: UILabel!
|
||||
|
||||
func update(theme: Theme) {
|
||||
name.textColor = theme.textPrimaryColor
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,16 @@
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
/**
|
||||
TODO: This view as it is implemented in this class must disappear.
|
||||
It should be part of the device verification flow (`DeviceVerificationCoordinator`).
|
||||
*/
|
||||
@interface EncryptionInfoView : MXKEncryptionInfoView
|
||||
|
||||
/**
|
||||
Open the legacy simple verification screen
|
||||
*/
|
||||
- (void)displayLegacyVerificationScreen;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -20,6 +20,15 @@
|
||||
#import "ThemeService.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface EncryptionInfoView() <DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
DeviceVerificationCoordinatorBridgePresenter *deviceVerificationCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation EncryptionInfoView
|
||||
|
||||
#pragma mark - Override MXKView
|
||||
@@ -37,4 +46,38 @@
|
||||
self.confirmVerifyButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
}
|
||||
|
||||
- (void)displayLegacyVerificationScreen
|
||||
{
|
||||
[super onButtonPressed:self.verifyButton];
|
||||
}
|
||||
|
||||
- (void)onButtonPressed:(id)sender
|
||||
{
|
||||
UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController;
|
||||
if (sender == self.verifyButton && self.mxDeviceInfo.verified != MXDeviceVerified
|
||||
&& self.mxDeviceInfo
|
||||
&& rootViewController)
|
||||
{
|
||||
// Redirect to the interactive device verification flow
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:self.mxSession];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
// Show it on the root view controller
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:rootViewController otherUserId:self.mxDeviceInfo.userId otherDeviceId:self.mxDeviceInfo.deviceId animated:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super onButtonPressed:sender];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deviceVerificationCoordinatorBridgePresenterDelegateDidComplete:(DeviceVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId {
|
||||
|
||||
[deviceVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
deviceVerificationCoordinatorBridgePresenter = nil;
|
||||
|
||||
// Eject like MXKEncryptionInfoView does
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -232,8 +232,8 @@
|
||||
#pragma mark - Private methods
|
||||
|
||||
- (void)openRoomWithId:(NSString*)roomId inMatrixSession:(MXSession*)mxSession
|
||||
{
|
||||
[[AppDelegate theDelegate].masterTabBarController selectRoomWithId:roomId andEventId:nil inMatrixSession:mxSession];
|
||||
{
|
||||
[[AppDelegate theDelegate] showRoom:roomId andEventId:nil withMatrixSession:mxSession restoreInitialDisplay:NO];
|
||||
}
|
||||
|
||||
- (void)refreshCurrentSelectedCell:(BOOL)forceVisible
|
||||
|
||||
@@ -135,14 +135,7 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
||||
}
|
||||
|
||||
// Use bold font for the room title
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont boldSystemFontOfSize:13];
|
||||
}
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold];
|
||||
}
|
||||
else if (roomCellData.roomSummary.room.summary.membership == MXMembershipInvite)
|
||||
{
|
||||
@@ -155,26 +148,13 @@ static const CGFloat kDirectRoomBorderWidth = 3.0;
|
||||
self.missedNotifAndUnreadBadgeBgViewWidthConstraint.constant = self.missedNotifAndUnreadBadgeLabel.frame.size.width + 18;
|
||||
|
||||
// Use bold font for the room title
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont boldSystemFontOfSize:13];
|
||||
}
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The room title is not bold anymore
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13];
|
||||
}
|
||||
// The room title is not bold anymore
|
||||
self.roomTitle.font = self.roomTitle1.font = self.roomTitle2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
|
||||
|
||||
}
|
||||
|
||||
self.directRoomBorderView.hidden = !roomCellData.roomSummary.room.isDirect;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 JitsiMeet
|
||||
|
||||
/// JitsiService enables to abstract and configure Jitsi Meet SDK
|
||||
@objcMembers
|
||||
final class JitsiService: NSObject {
|
||||
|
||||
static let shared = JitsiService()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var enableCallKit: Bool = true {
|
||||
didSet {
|
||||
JMCallKitProxy.enabled = enableCallKit
|
||||
}
|
||||
}
|
||||
|
||||
private let jitsiMeet = JitsiMeet.sharedInstance()
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
// MARK: Configuration
|
||||
|
||||
func configureDefaultConferenceOptions(with serverURL: URL) {
|
||||
self.jitsiMeet.defaultConferenceOptions = JitsiMeetConferenceOptions.fromBuilder({ (builder) in
|
||||
builder.serverURL = serverURL
|
||||
})
|
||||
}
|
||||
|
||||
func configureCallKitProvider(localizedName: String, ringtoneName: String?, iconTemplateImageData: Data?) {
|
||||
JMCallKitProxy.configureProvider(localizedName: localizedName, ringtoneSound: ringtoneName, iconTemplateImageData: iconTemplateImageData)
|
||||
}
|
||||
|
||||
// MARK: AppDelegate methods
|
||||
|
||||
@discardableResult
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
return self.jitsiMeet.application(application, didFinishLaunchingWithOptions: launchOptions ?? [:])
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||
return self.jitsiMeet.application(application, open: url, options: options)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||
return self.jitsiMeet.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import <JitsiMeet/JitsiMeet.h>
|
||||
|
||||
#import "WidgetManager.h"
|
||||
|
||||
@protocol JitsiViewControllerDelegate;
|
||||
@@ -28,7 +26,7 @@
|
||||
|
||||
https://github.com/jitsi/jitsi-meet/tree/master/ios
|
||||
*/
|
||||
@interface JitsiViewController : MXKViewController <JitsiMeetViewDelegate>
|
||||
@interface JitsiViewController : MXKViewController
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for a `JitsiViewController`.
|
||||
@@ -75,12 +73,6 @@
|
||||
*/
|
||||
@property (nonatomic) id<JitsiViewControllerDelegate> delegate;
|
||||
|
||||
#pragma mark - Xib attributes
|
||||
|
||||
// The jitsi-meet SDK view
|
||||
@property (weak, nonatomic) IBOutlet JitsiMeetView *jitsiMeetView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *backToAppButton;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -15,15 +15,17 @@
|
||||
*/
|
||||
|
||||
#import "JitsiViewController.h"
|
||||
@import JitsiMeet;
|
||||
|
||||
static const NSString *kJitsiServerUrl = @"https://jitsi.riot.im/";
|
||||
static const NSString *kJitsiDataErrorKey = @"error";
|
||||
|
||||
@interface JitsiViewController ()
|
||||
{
|
||||
NSString *jitsiUrl;
|
||||
@interface JitsiViewController () <JitsiMeetViewDelegate>
|
||||
|
||||
BOOL video;
|
||||
}
|
||||
// The jitsi-meet SDK view
|
||||
@property (nonatomic, weak) IBOutlet JitsiMeetView *jitsiMeetView;
|
||||
|
||||
@property (nonatomic, strong) NSString *conferenceId;
|
||||
@property (nonatomic) BOOL startWithVideo;
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,14 +46,41 @@ static const NSString *kJitsiServerUrl = @"https://jitsi.riot.im/";
|
||||
return jitsiViewController;
|
||||
}
|
||||
|
||||
#pragma mark - Life cycle
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.jitsiMeetView.delegate = self;
|
||||
|
||||
[self joinConference];
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)openWidget:(Widget*)widget withVideo:(BOOL)aVideo
|
||||
success:(void (^)(void))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
{
|
||||
video = aVideo;
|
||||
self.startWithVideo = aVideo;
|
||||
_widget = widget;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
[_widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// Extract the jitsi conference id from the widget url
|
||||
NSString *confId;
|
||||
@@ -69,15 +98,11 @@ static const NSString *kJitsiServerUrl = @"https://jitsi.riot.im/";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// And build from it the url to use in jitsi-meet sdk
|
||||
if (confId)
|
||||
{
|
||||
jitsiUrl = [NSString stringWithFormat:@"%@%@", kJitsiServerUrl, confId];
|
||||
}
|
||||
}
|
||||
|
||||
self.conferenceId = confId;
|
||||
|
||||
if (jitsiUrl)
|
||||
if (confId)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
@@ -107,71 +132,72 @@ static const NSString *kJitsiServerUrl = @"https://jitsi.riot.im/";
|
||||
|
||||
- (void)hangup
|
||||
{
|
||||
jitsiUrl = nil;
|
||||
[self.jitsiMeetView leave];
|
||||
}
|
||||
|
||||
// It would have been nicer to ask JitsiMeetView but there is no api.
|
||||
// Dismissing the view controller and releasing it does the job for the moment
|
||||
if (_delegate)
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)joinConference
|
||||
{
|
||||
[self joinConferenceWithId:self.conferenceId];
|
||||
}
|
||||
|
||||
- (void)joinConferenceWithId:(NSString*)conferenceId
|
||||
{
|
||||
if (conferenceId)
|
||||
{
|
||||
[_delegate jitsiViewController:self dismissViewJitsiController:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.jitsiMeetView.delegate = self;
|
||||
|
||||
// Pass the URL to jitsi-meet sdk
|
||||
[self.jitsiMeetView loadURLObject: @{
|
||||
@"url": jitsiUrl,
|
||||
@"configOverwrite": @{
|
||||
@"startWithVideoMuted": @(!video)
|
||||
}
|
||||
}];
|
||||
|
||||
// TODO: Set up user info but it is not yet available in the jitsi-meet iOS SDK
|
||||
// See https://github.com/jitsi/jitsi-meet/issues/1880
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)onBackToAppButtonPressed:(id)sender
|
||||
{
|
||||
if (_delegate)
|
||||
{
|
||||
[_delegate jitsiViewController:self goBackToApp:nil];
|
||||
// TODO: Set up user info but it is not yet available in the jitsi-meet iOS SDK
|
||||
// See https://github.com/jitsi/jitsi-meet/issues/1880
|
||||
|
||||
JitsiMeetConferenceOptions *jitsiMeetConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder * _Nonnull jitsiMeetConferenceOptionsBuilder) {
|
||||
jitsiMeetConferenceOptionsBuilder.room = conferenceId;
|
||||
jitsiMeetConferenceOptionsBuilder.videoMuted = !self.startWithVideo;
|
||||
}];
|
||||
|
||||
[self.jitsiMeetView join:jitsiMeetConferenceOptions];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - JitsiMeetViewDelegate
|
||||
|
||||
- (void)conferenceFailed:(NSDictionary *)data
|
||||
- (void)conferenceWillJoin:(NSDictionary *)data
|
||||
{
|
||||
NSLog(@"[JitsiViewController] conferenceFailed - data: %@", data);
|
||||
}
|
||||
|
||||
- (void)conferenceLeft:(NSDictionary *)data
|
||||
- (void)conferenceJoined:(NSDictionary *)data
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
// The conference is over. Let the delegate close this view controller.
|
||||
if (_delegate)
|
||||
{
|
||||
[_delegate jitsiViewController:self dismissViewJitsiController:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do it ourself
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)conferenceTerminated:(NSDictionary *)data
|
||||
{
|
||||
if (data[kJitsiDataErrorKey] != nil)
|
||||
{
|
||||
NSLog(@"[JitsiViewController] conferenceTerminated - data: %@", data);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
// The conference is over. Let the delegate close this view controller.
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate jitsiViewController:self dismissViewJitsiController:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do it ourself
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enterPictureInPicture:(NSDictionary *)data
|
||||
{
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate jitsiViewController:self goBackToApp:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.70"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JitsiViewController">
|
||||
<connections>
|
||||
<outlet property="backToAppButton" destination="8tr-Cb-ue8" id="aUj-co-7JA"/>
|
||||
<outlet property="jitsiMeetView" destination="7hL-Cs-mak" id="7kR-Te-Klw"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
@@ -25,41 +24,14 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8tr-Cb-ue8">
|
||||
<rect key="frame" x="10" y="5" width="44" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCBackToAppButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="3id-6Q-PUF"/>
|
||||
<constraint firstAttribute="width" constant="44" id="JGj-Jz-SbU"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="13"/>
|
||||
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<inset key="titleEdgeInsets" minX="-69" minY="61" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="back_icon">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onBackToAppButtonPressed:" destination="-1" eventType="touchUpInside" id="2wo-nB-Rwd"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="7hL-Cs-mak" secondAttribute="trailing" id="8eH-2r-pjD"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7hL-Cs-mak" secondAttribute="bottom" id="BAo-6X-ovC"/>
|
||||
<constraint firstItem="8tr-Cb-ue8" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="5" id="FPS-wy-gK6"/>
|
||||
<constraint firstItem="8tr-Cb-ue8" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="10" id="Xca-R4-1cu"/>
|
||||
<constraint firstItem="7hL-Cs-mak" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="s46-Fx-tT8"/>
|
||||
<constraint firstItem="7hL-Cs-mak" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="x3v-Xl-cKi"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="back_icon" width="13" height="23"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -83,7 +83,7 @@ final class KeyBackupBannerCell: MXKTableViewCell {
|
||||
subtitle = VectorL10n.keyBackupSetupBannerSubtitle
|
||||
case .recover:
|
||||
title = VectorL10n.keyBackupRecoverBannerTitle
|
||||
subtitle = VectorL10n.keyBackupRecoverBannerSubtitle
|
||||
subtitle = VectorL10n.keyBackupRecoverConnentBannerSubtitle
|
||||
case .none:
|
||||
title = nil
|
||||
subtitle = nil
|
||||
|
||||
@@ -95,7 +95,7 @@ final class KeyBackupSetupIntroViewController: UIViewController {
|
||||
self.titleLabel.text = VectorL10n.keyBackupSetupIntroTitle
|
||||
self.informationLabel.text = VectorL10n.keyBackupSetupIntroInfo
|
||||
|
||||
let setupTitle = self.isABackupAlreadyExists ? VectorL10n.keyBackupSetupIntroSetupActionWithExistingBackup : VectorL10n.keyBackupSetupIntroSetupActionWithoutExistingBackup
|
||||
let setupTitle = self.isABackupAlreadyExists ? VectorL10n.keyBackupSetupIntroSetupConnectActionWithExistingBackup : VectorL10n.keyBackupSetupIntroSetupActionWithoutExistingBackup
|
||||
|
||||
self.setUpButton.setTitle(setupTitle, for: .normal)
|
||||
|
||||
|
||||
@@ -75,32 +75,10 @@
|
||||
*/
|
||||
+ (instancetype)mediaPickerViewController;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIScrollView *mainScrollView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *captureViewContainer;
|
||||
//@property (weak, nonatomic) IBOutlet NSLayoutConstraint *captureViewContainerHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewContainerView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraPreviewContainerAspectRatio;
|
||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *cameraActivityIndicator;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cameraSwitchButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cameraCaptureButton;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraCaptureButtonWidthConstraint;
|
||||
@property (weak, nonatomic) IBOutlet MXKPieChartView *cameraVideoCaptureProgressView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *recentCapturesCollectionContainerView;
|
||||
@property (weak, nonatomic) IBOutlet UICollectionView *recentCapturesCollectionView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recentCapturesCollectionContainerViewHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *libraryViewContainer;
|
||||
@property (weak, nonatomic) IBOutlet UITableView *userAlbumsTableView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *libraryViewContainerViewHeightConstraint;
|
||||
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<MediaPickerViewControllerDelegate> delegate;
|
||||
@property (nonatomic, weak) id<MediaPickerViewControllerDelegate> delegate;
|
||||
|
||||
/**
|
||||
The array of the media types supported by the picker (default value is an array containing kUTTypeImage).
|
||||
|
||||
@@ -94,6 +94,27 @@ static void *RecordingContext = &RecordingContext;
|
||||
BOOL isStatusBarHidden;
|
||||
}
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIScrollView *mainScrollView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *captureViewContainer;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewContainerView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraPreviewContainerAspectRatio;
|
||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *cameraActivityIndicator;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cameraSwitchButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cameraCaptureButton;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraCaptureButtonWidthConstraint;
|
||||
@property (weak, nonatomic) IBOutlet MXKPieChartView *cameraVideoCaptureProgressView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *recentCapturesCollectionContainerView;
|
||||
@property (weak, nonatomic) IBOutlet UICollectionView *recentCapturesCollectionView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recentCapturesCollectionContainerViewHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *libraryViewContainer;
|
||||
@property (weak, nonatomic) IBOutlet UITableView *userAlbumsTableView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *libraryViewContainerViewHeightConstraint;
|
||||
|
||||
@property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID;
|
||||
|
||||
@end
|
||||
@@ -163,19 +184,24 @@ static void *RecordingContext = &RecordingContext;
|
||||
|
||||
[self setBackgroundRecordingID:UIBackgroundTaskInvalid];
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
// Observe UIApplicationWillEnterForegroundNotification to refresh captures collection when app leaves the background state.
|
||||
UIApplicationWillEnterForegroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self reloadRecentCapturesCollection];
|
||||
[self reloadUserLibraryAlbums];
|
||||
|
||||
|
||||
}];
|
||||
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
}];
|
||||
[self userInterfaceThemeDidChange];
|
||||
}
|
||||
@@ -292,7 +318,7 @@ static void *RecordingContext = &RecordingContext;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
|
||||
[self.cameraActivityIndicator stopAnimating];
|
||||
cameraPreviewLayer.hidden = NO;
|
||||
self->cameraPreviewLayer.hidden = NO;
|
||||
|
||||
});
|
||||
});
|
||||
@@ -318,7 +344,7 @@ static void *RecordingContext = &RecordingContext;
|
||||
if (granted)
|
||||
{
|
||||
// Load recent captures if this is not already done
|
||||
if (!recentCaptures.count)
|
||||
if (!self->recentCaptures.count)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
@@ -564,9 +590,13 @@ static void *RecordingContext = &RecordingContext;
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
dispatch_async(userAlbumsQueue, ^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// List user albums which are not empty
|
||||
PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
|
||||
|
||||
@@ -575,9 +605,9 @@ static void *RecordingContext = &RecordingContext;
|
||||
|
||||
// Set up fetch options.
|
||||
PHFetchOptions *options = [[PHFetchOptions alloc] init];
|
||||
if ([_mediaTypes indexOfObject:(NSString *)kUTTypeImage] != NSNotFound)
|
||||
if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeImage] != NSNotFound)
|
||||
{
|
||||
if ([_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
|
||||
if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
|
||||
{
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"(mediaType == %d) || (mediaType == %d)", PHAssetMediaTypeImage, PHAssetMediaTypeVideo];
|
||||
}
|
||||
@@ -586,7 +616,7 @@ static void *RecordingContext = &RecordingContext;
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage];
|
||||
}
|
||||
}
|
||||
else if ([_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
|
||||
else if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
|
||||
{
|
||||
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeVideo];
|
||||
}
|
||||
@@ -626,11 +656,11 @@ static void *RecordingContext = &RecordingContext;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
userAlbums = updatedUserAlbums;
|
||||
if (userAlbums.count)
|
||||
self->userAlbums = updatedUserAlbums;
|
||||
if (self->userAlbums.count)
|
||||
{
|
||||
self.userAlbumsTableView.hidden = NO;
|
||||
self.libraryViewContainerViewHeightConstraint.constant = (userAlbums.count * 74);
|
||||
self.libraryViewContainerViewHeightConstraint.constant = (self->userAlbums.count * 74);
|
||||
[self.libraryViewContainer needsUpdateConstraints];
|
||||
|
||||
[self.userAlbumsTableView reloadData];
|
||||
@@ -759,13 +789,13 @@ static void *RecordingContext = &RecordingContext;
|
||||
}];
|
||||
}
|
||||
|
||||
isValidationInProgress = NO;
|
||||
self->isValidationInProgress = NO;
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset");
|
||||
isValidationInProgress = NO;
|
||||
self->isValidationInProgress = NO;
|
||||
|
||||
// Alert user
|
||||
NSError *error = info[@"PHImageErrorKey"];
|
||||
@@ -815,20 +845,20 @@ static void *RecordingContext = &RecordingContext;
|
||||
[self.delegate mediaPickerController:self didSelectVideo:[avURLAsset URL]];
|
||||
}
|
||||
|
||||
isValidationInProgress = NO;
|
||||
self->isValidationInProgress = NO;
|
||||
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] Selected video asset is not initialized from an URL!");
|
||||
isValidationInProgress = NO;
|
||||
self->isValidationInProgress = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset");
|
||||
isValidationInProgress = NO;
|
||||
self->isValidationInProgress = NO;
|
||||
|
||||
// Alert user
|
||||
NSError *error = info[@"PHImageErrorKey"];
|
||||
@@ -1103,8 +1133,12 @@ static void *RecordingContext = &RecordingContext;
|
||||
|
||||
[self.cameraActivityIndicator startAnimating];
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
dispatch_async(cameraQueue, ^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// Get the Camera Device
|
||||
AVCaptureDevice *frontCamera = nil;
|
||||
AVCaptureDevice *backCamera = nil;
|
||||
@@ -1151,42 +1185,42 @@ static void *RecordingContext = &RecordingContext;
|
||||
}
|
||||
}
|
||||
|
||||
currentCameraInput = nil;
|
||||
self->currentCameraInput = nil;
|
||||
NSError *error = nil;
|
||||
if (frontCamera)
|
||||
{
|
||||
frontCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:frontCamera error:&error];
|
||||
self->frontCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:frontCamera error:&error];
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] Error: %@", error);
|
||||
}
|
||||
|
||||
if (frontCameraInput == nil)
|
||||
if (self->frontCameraInput == nil)
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] Error creating front camera capture input");
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCameraInput = frontCameraInput;
|
||||
self->currentCameraInput = self->frontCameraInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (backCamera)
|
||||
{
|
||||
error = nil;
|
||||
backCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:backCamera error:&error];
|
||||
self->backCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:backCamera error:&error];
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] Error: %@", error);
|
||||
}
|
||||
|
||||
if (backCameraInput == nil)
|
||||
if (self->backCameraInput == nil)
|
||||
{
|
||||
NSLog(@"[MediaPickerVC] Error creating back camera capture input");
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCameraInput = backCameraInput;
|
||||
self->currentCameraInput = self->backCameraInput;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,38 +1228,38 @@ static void *RecordingContext = &RecordingContext;
|
||||
self.cameraSwitchButton.hidden = (!frontCamera || !backCamera);
|
||||
});
|
||||
|
||||
if (currentCameraInput)
|
||||
if (self->currentCameraInput)
|
||||
{
|
||||
// Create the AVCapture Session
|
||||
captureSession = [[AVCaptureSession alloc] init];
|
||||
self->captureSession = [[AVCaptureSession alloc] init];
|
||||
|
||||
if (isPictureCaptureEnabled)
|
||||
if (self->isPictureCaptureEnabled)
|
||||
{
|
||||
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
|
||||
[self->captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
|
||||
}
|
||||
else if (isVideoCaptureEnabled)
|
||||
else if (self->isVideoCaptureEnabled)
|
||||
{
|
||||
[captureSession setSessionPreset:AVCaptureSessionPresetHigh];
|
||||
[self->captureSession setSessionPreset:AVCaptureSessionPresetHigh];
|
||||
}
|
||||
|
||||
cameraPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
|
||||
cameraPreviewLayer.masksToBounds = NO;
|
||||
cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//AVLayerVideoGravityResizeAspect;
|
||||
cameraPreviewLayer.backgroundColor = [[UIColor blackColor] CGColor];
|
||||
self->cameraPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self->captureSession];
|
||||
self->cameraPreviewLayer.masksToBounds = NO;
|
||||
self->cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//AVLayerVideoGravityResizeAspect;
|
||||
self->cameraPreviewLayer.backgroundColor = [[UIColor blackColor] CGColor];
|
||||
// cameraPreviewLayer.borderWidth = 2;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[[cameraPreviewLayer connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]];
|
||||
[cameraPreviewLayer connection].videoScaleAndCropFactor = 1.0;
|
||||
cameraPreviewLayer.frame = self.cameraPreviewContainerView.bounds;
|
||||
cameraPreviewLayer.hidden = YES;
|
||||
[[self->cameraPreviewLayer connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]];
|
||||
[self->cameraPreviewLayer connection].videoScaleAndCropFactor = 1.0;
|
||||
self->cameraPreviewLayer.frame = self.cameraPreviewContainerView.bounds;
|
||||
self->cameraPreviewLayer.hidden = YES;
|
||||
|
||||
[self.cameraPreviewContainerView.layer addSublayer:cameraPreviewLayer];
|
||||
[self.cameraPreviewContainerView.layer addSublayer:self->cameraPreviewLayer];
|
||||
|
||||
});
|
||||
|
||||
[captureSession addInput:currentCameraInput];
|
||||
[self->captureSession addInput:self->currentCameraInput];
|
||||
|
||||
AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
|
||||
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
|
||||
@@ -1235,36 +1269,36 @@ static void *RecordingContext = &RecordingContext;
|
||||
NSLog(@"[MediaPickerVC] Error: %@", error);
|
||||
}
|
||||
|
||||
if ([captureSession canAddInput:audioDeviceInput])
|
||||
if ([self->captureSession canAddInput:audioDeviceInput])
|
||||
{
|
||||
[captureSession addInput:audioDeviceInput];
|
||||
[self->captureSession addInput:audioDeviceInput];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(caughtAVRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVCaptureSessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:nil];
|
||||
|
||||
[captureSession startRunning];
|
||||
[self->captureSession startRunning];
|
||||
|
||||
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
|
||||
if ([captureSession canAddOutput:movieFileOutput])
|
||||
self->movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
|
||||
if ([self->captureSession canAddOutput:self->movieFileOutput])
|
||||
{
|
||||
[captureSession addOutput:movieFileOutput];
|
||||
AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
|
||||
[self->captureSession addOutput:self->movieFileOutput];
|
||||
AVCaptureConnection *connection = [self->movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
|
||||
if ([connection isVideoStabilizationSupported])
|
||||
{
|
||||
// Available on iOS 8 and later
|
||||
[connection setPreferredVideoStabilizationMode:YES];
|
||||
}
|
||||
}
|
||||
[movieFileOutput addObserver:self forKeyPath:@"recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
|
||||
[self->movieFileOutput addObserver:self forKeyPath:@"recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
|
||||
|
||||
stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
||||
if ([captureSession canAddOutput:stillImageOutput])
|
||||
self->stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
||||
if ([self->captureSession canAddOutput:self->stillImageOutput])
|
||||
{
|
||||
[stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}];
|
||||
[captureSession addOutput:stillImageOutput];
|
||||
[self->stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}];
|
||||
[self->captureSession addOutput:self->stillImageOutput];
|
||||
}
|
||||
[stillImageOutput addObserver:self forKeyPath:@"capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
|
||||
[self->stillImageOutput addObserver:self forKeyPath:@"capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1273,7 +1307,7 @@ static void *RecordingContext = &RecordingContext;
|
||||
});
|
||||
}
|
||||
|
||||
isCaptureSessionSetupInProgress = NO;
|
||||
self->isCaptureSessionSetupInProgress = NO;
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1300,23 +1334,24 @@ static void *RecordingContext = &RecordingContext;
|
||||
}
|
||||
|
||||
dispatch_sync(cameraQueue, ^{
|
||||
frontCameraInput = nil;
|
||||
backCameraInput = nil;
|
||||
captureSession = nil;
|
||||
|
||||
self->frontCameraInput = nil;
|
||||
self->backCameraInput = nil;
|
||||
self->captureSession = nil;
|
||||
|
||||
if (movieFileOutput)
|
||||
if (self->movieFileOutput)
|
||||
{
|
||||
[movieFileOutput removeObserver:self forKeyPath:@"recording" context:RecordingContext];
|
||||
movieFileOutput = nil;
|
||||
[self->movieFileOutput removeObserver:self forKeyPath:@"recording" context:RecordingContext];
|
||||
self->movieFileOutput = nil;
|
||||
}
|
||||
|
||||
if (stillImageOutput)
|
||||
if (self->stillImageOutput)
|
||||
{
|
||||
[stillImageOutput removeObserver:self forKeyPath:@"capturingStillImage" context:CapturingStillImageContext];
|
||||
stillImageOutput = nil;
|
||||
[self->stillImageOutput removeObserver:self forKeyPath:@"capturingStillImage" context:CapturingStillImageContext];
|
||||
self->stillImageOutput = nil;
|
||||
}
|
||||
|
||||
currentCameraInput = nil;
|
||||
self->currentCameraInput = nil;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:nil];
|
||||
@@ -1342,7 +1377,7 @@ static void *RecordingContext = &RecordingContext;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
|
||||
[self.cameraActivityIndicator stopAnimating];
|
||||
cameraPreviewLayer.hidden = NO;
|
||||
self->cameraPreviewLayer.hidden = NO;
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1357,45 +1392,45 @@ static void *RecordingContext = &RecordingContext;
|
||||
if (frontCameraInput && backCameraInput)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!canToggleCamera)
|
||||
if (!self->canToggleCamera)
|
||||
{
|
||||
return;
|
||||
}
|
||||
canToggleCamera = NO;
|
||||
self->canToggleCamera = NO;
|
||||
|
||||
AVCaptureDeviceInput *newInput = nil;
|
||||
AVCaptureDeviceInput *oldInput = nil;
|
||||
if (currentCameraInput == frontCameraInput)
|
||||
if (self->currentCameraInput == self->frontCameraInput)
|
||||
{
|
||||
newInput = backCameraInput;
|
||||
oldInput = frontCameraInput;
|
||||
newInput = self->backCameraInput;
|
||||
oldInput = self->frontCameraInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
newInput = frontCameraInput;
|
||||
oldInput = backCameraInput;
|
||||
newInput = self->frontCameraInput;
|
||||
oldInput = self->backCameraInput;
|
||||
}
|
||||
|
||||
dispatch_async(cameraQueue, ^{
|
||||
dispatch_async(self->cameraQueue, ^{
|
||||
|
||||
[captureSession beginConfiguration];
|
||||
[captureSession removeInput:oldInput];
|
||||
if ([captureSession canAddInput:newInput]) {
|
||||
[captureSession addInput:newInput];
|
||||
currentCameraInput = newInput;
|
||||
[self->captureSession beginConfiguration];
|
||||
[self->captureSession removeInput:oldInput];
|
||||
if ([self->captureSession canAddInput:newInput]) {
|
||||
[self->captureSession addInput:newInput];
|
||||
self->currentCameraInput = newInput;
|
||||
}
|
||||
[captureSession commitConfiguration];
|
||||
[self->captureSession commitConfiguration];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[self.cameraActivityIndicator stopAnimating];
|
||||
cameraPreviewLayer.hidden = NO;
|
||||
canToggleCamera = YES;
|
||||
self->cameraPreviewLayer.hidden = NO;
|
||||
self->canToggleCamera = YES;
|
||||
});
|
||||
});
|
||||
|
||||
[self.cameraActivityIndicator startAnimating];
|
||||
cameraPreviewLayer.hidden = YES;
|
||||
self->cameraPreviewLayer.hidden = YES;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1404,10 +1439,15 @@ static void *RecordingContext = &RecordingContext;
|
||||
{
|
||||
self.cameraCaptureButton.enabled = NO;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
dispatch_async(cameraQueue, ^{
|
||||
if (![movieFileOutput isRecording])
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (![self->movieFileOutput isRecording])
|
||||
{
|
||||
lockInterfaceRotation = YES;
|
||||
self->lockInterfaceRotation = YES;
|
||||
|
||||
if ([[UIDevice currentDevice] isMultitaskingSupported])
|
||||
{
|
||||
@@ -1425,14 +1465,14 @@ static void *RecordingContext = &RecordingContext;
|
||||
}
|
||||
|
||||
// Update the orientation on the movie file output video connection before starting recording.
|
||||
[[movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[cameraPreviewLayer connection] videoOrientation]];
|
||||
[[self->movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[self->cameraPreviewLayer connection] videoOrientation]];
|
||||
|
||||
// Turning OFF flash for video recording
|
||||
[MediaPickerViewController setFlashMode:AVCaptureFlashModeOff forDevice:[currentCameraInput device]];
|
||||
[MediaPickerViewController setFlashMode:AVCaptureFlashModeOff forDevice:[self->currentCameraInput device]];
|
||||
|
||||
// Start recording to a temporary file.
|
||||
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"movie" stringByAppendingPathExtension:@"mov"]];
|
||||
[movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
|
||||
[self->movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1440,9 +1480,10 @@ static void *RecordingContext = &RecordingContext;
|
||||
- (void)stopMovieRecording
|
||||
{
|
||||
dispatch_async(cameraQueue, ^{
|
||||
if ([movieFileOutput isRecording])
|
||||
|
||||
if ([self->movieFileOutput isRecording])
|
||||
{
|
||||
[movieFileOutput stopRecording];
|
||||
[self->movieFileOutput stopRecording];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1451,15 +1492,20 @@ static void *RecordingContext = &RecordingContext;
|
||||
{
|
||||
self.cameraCaptureButton.enabled = NO;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
dispatch_async(cameraQueue, ^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// Update the orientation on the still image output video connection before capturing.
|
||||
[[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[cameraPreviewLayer connection] videoOrientation]];
|
||||
[[self->stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[self->cameraPreviewLayer connection] videoOrientation]];
|
||||
|
||||
// Flash set to Auto for Still Capture
|
||||
[MediaPickerViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[currentCameraInput device]];
|
||||
[MediaPickerViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[self->currentCameraInput device]];
|
||||
|
||||
// Capture a still image.
|
||||
[stillImageOutput captureStillImageAsynchronouslyFromConnection:[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
|
||||
[self->stillImageOutput captureStillImageAsynchronouslyFromConnection:[self->stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
|
||||
|
||||
if (imageDataSampleBuffer)
|
||||
{
|
||||
@@ -1504,10 +1550,10 @@ static void *RecordingContext = &RecordingContext;
|
||||
- (void)runStillImageCaptureAnimation
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[cameraPreviewLayer setOpacity:0.0];
|
||||
[self->cameraPreviewLayer setOpacity:0.0];
|
||||
|
||||
[UIView animateWithDuration:.25 animations:^{
|
||||
[cameraPreviewLayer setOpacity:1.0];
|
||||
[self->cameraPreviewLayer setOpacity:1.0];
|
||||
}];
|
||||
});
|
||||
}
|
||||
@@ -1535,18 +1581,18 @@ static void *RecordingContext = &RecordingContext;
|
||||
{
|
||||
self.cameraSwitchButton.enabled = NO;
|
||||
|
||||
videoRecordStartDate = [NSDate date];
|
||||
self->videoRecordStartDate = [NSDate date];
|
||||
|
||||
self.cameraVideoCaptureProgressView.hidden = NO;
|
||||
updateVideoRecordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateVideoRecordingDuration) userInfo:nil repeats:YES];
|
||||
self->updateVideoRecordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateVideoRecordingDuration) userInfo:nil repeats:YES];
|
||||
|
||||
self.cameraCaptureButton.enabled = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.cameraVideoCaptureProgressView.hidden = YES;
|
||||
[updateVideoRecordingTimer invalidate];
|
||||
updateVideoRecordingTimer = nil;
|
||||
[self->updateVideoRecordingTimer invalidate];
|
||||
self->updateVideoRecordingTimer = nil;
|
||||
self.cameraVideoCaptureProgressView.progress = 0;
|
||||
|
||||
// The preview will be restored during captureOutput:didFinishRecordingToOutputFileAtURL: callback.
|
||||
|
||||
@@ -314,6 +314,8 @@
|
||||
// Set the container tag to be able to retrieve read receipts container from component index (see component selection in MXKRoomBubbleTableViewCell (Vector) category).
|
||||
avatarsContainer.tag = index;
|
||||
|
||||
avatarsContainer.moreLabelTextColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
[avatarsContainer refreshReceiptSenders:roomMembers withPlaceHolders:placeholders andAlignment:ReadReceiptAlignmentRight];
|
||||
avatarsContainer.readReceipts = receipts;
|
||||
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:cell action:@selector(onReceiptContainerTap:)];
|
||||
|
||||
@@ -29,13 +29,11 @@
|
||||
#import "TableViewCellWithButton.h"
|
||||
#import "RoomTableViewCell.h"
|
||||
|
||||
#import "EncryptionInfoView.h"
|
||||
|
||||
#define TABLEVIEW_ROW_CELL_HEIGHT 46
|
||||
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
|
||||
#define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f
|
||||
|
||||
@interface RoomMemberDetailsViewController () <RoomMemberTitleViewDelegate>
|
||||
@interface RoomMemberDetailsViewController () <RoomMemberTitleViewDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
RoomMemberTitleView* memberTitleView;
|
||||
|
||||
@@ -62,7 +60,8 @@
|
||||
*/
|
||||
NSArray<MXDeviceInfo *> *devicesArray;
|
||||
NSInteger devicesIndex;
|
||||
EncryptionInfoView *encryptionInfoView;
|
||||
DeviceVerificationCoordinatorBridgePresenter *deviceVerificationCoordinatorBridgePresenter;
|
||||
|
||||
|
||||
/**
|
||||
Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
|
||||
@@ -319,19 +318,6 @@
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
// Check here whether a subview has been added or removed
|
||||
if (encryptionInfoView)
|
||||
{
|
||||
if (!encryptionInfoView.superview)
|
||||
{
|
||||
// Reset
|
||||
encryptionInfoView = nil;
|
||||
|
||||
// Reload the full table to take into account a potential change on a device status.
|
||||
[self updateMemberInfo];
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the title view has been created and rendered.
|
||||
if (memberTitleView && memberTitleView.superview)
|
||||
{
|
||||
@@ -1154,50 +1140,10 @@
|
||||
{
|
||||
if (verificationStatus == MXDeviceVerified)
|
||||
{
|
||||
// Prompt the user before marking as verified the device.
|
||||
encryptionInfoView = [[EncryptionInfoView alloc] initWithDeviceInfo:deviceTableViewCell.deviceInfo andMatrixSession:self.mxRoom.mxSession];
|
||||
[encryptionInfoView onButtonPressed:encryptionInfoView.verifyButton];
|
||||
|
||||
// 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
|
||||
[self.view addSubview:encryptionInfoView];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0f
|
||||
constant:10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.tableView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0f
|
||||
constant:-10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.tableView
|
||||
attribute:NSLayoutAttributeLeading
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeLeading
|
||||
multiplier:1.0f
|
||||
constant:-10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.tableView
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1.0f
|
||||
constant:10.0f]];
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:self otherUserId:deviceTableViewCell.deviceInfo.userId otherDeviceId:deviceTableViewCell.deviceInfo.deviceId animated:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1217,4 +1163,12 @@
|
||||
[self viewDidLayoutSubviews];
|
||||
}
|
||||
|
||||
#pragma mark - DeviceVerificationCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)deviceVerificationCoordinatorBridgePresenterDelegateDidComplete:(DeviceVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
[deviceVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
deviceVerificationCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -3561,6 +3561,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[removedAlias addObject:roomAlias];
|
||||
}
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
NSMutableIndexSet *mutableIndexSet = [NSMutableIndexSet indexSet];
|
||||
|
||||
if (roomAddresses.count <= 1)
|
||||
@@ -3572,6 +3574,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[mutableIndexSet addIndex:ROOM_SETTINGS_ROOM_ADDRESSES_SECTION_INDEX];
|
||||
[self.tableView reloadSections:mutableIndexSet withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
|
||||
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
|
||||
}
|
||||
}
|
||||
@@ -3601,9 +3605,13 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[removedGroup addObject:groupId];
|
||||
}
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:ROOM_SETTINGS_RELATED_GROUPS_SECTION_INDEX];
|
||||
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
|
||||
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
|
||||
}
|
||||
|
||||
@@ -3636,6 +3644,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[addedAlias addObject:roomAlias];
|
||||
}
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
NSMutableIndexSet *mutableIndexSet = [NSMutableIndexSet indexSet];
|
||||
|
||||
if (!roomAddresses.count)
|
||||
@@ -3663,6 +3673,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[mutableIndexSet addIndex:ROOM_SETTINGS_ROOM_ADDRESSES_SECTION_INDEX];
|
||||
[self.tableView reloadSections:mutableIndexSet withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
|
||||
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
|
||||
|
||||
return YES;
|
||||
@@ -3726,9 +3738,13 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
[addedGroup addObject:groupId];
|
||||
}
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:ROOM_SETTINGS_RELATED_GROUPS_SECTION_INDEX];
|
||||
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
|
||||
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
|
||||
@interface RoomInputToolbarView()
|
||||
{
|
||||
MediaPickerViewController *mediaPicker;
|
||||
|
||||
// The intermediate action sheet
|
||||
UIAlertController *actionSheet;
|
||||
}
|
||||
|
||||
@property(nonatomic, weak) MediaPickerViewController *mediaPicker;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomInputToolbarView
|
||||
@@ -391,11 +391,13 @@
|
||||
Class PHAsset_class = NSClassFromString(@"PHAsset");
|
||||
if (PHAsset_class)
|
||||
{
|
||||
mediaPicker = [MediaPickerViewController mediaPickerViewController];
|
||||
MediaPickerViewController * mediaPicker = [MediaPickerViewController mediaPickerViewController];
|
||||
mediaPicker.mediaTypes = @[(NSString *)kUTTypeImage, (NSString *)kUTTypeMovie];
|
||||
mediaPicker.delegate = self;
|
||||
UINavigationController *navigationController = [UINavigationController new];
|
||||
[navigationController pushViewController:mediaPicker animated:NO];
|
||||
|
||||
self.mediaPicker = mediaPicker;
|
||||
|
||||
[self.delegate roomInputToolbarView:self presentViewController:navigationController];
|
||||
}
|
||||
@@ -448,11 +450,9 @@
|
||||
|
||||
- (void)dismissMediaPicker
|
||||
{
|
||||
if (mediaPicker)
|
||||
if (self.mediaPicker)
|
||||
{
|
||||
[mediaPicker withdrawViewControllerAnimated:YES completion:nil];
|
||||
[mediaPicker destroy];
|
||||
mediaPicker = nil;
|
||||
[self.mediaPicker withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
application asking the user if he wants to share room keys with a user's device.
|
||||
For the moment, the user is himself.
|
||||
*/
|
||||
@interface RoomKeyRequestViewController : NSObject <MXKEncryptionInfoViewDelegate>
|
||||
@interface RoomKeyRequestViewController : NSObject
|
||||
|
||||
/**
|
||||
The UIAlertController instance which handles the dialog.
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
#import "RoomKeyRequestViewController.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "EncryptionInfoView.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface RoomKeyRequestViewController ()
|
||||
@interface RoomKeyRequestViewController () <DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
void (^onComplete)(void);
|
||||
|
||||
EncryptionInfoView *encryptionInfoView;
|
||||
DeviceVerificationCoordinatorBridgePresenter *deviceVerificationCoordinatorBridgePresenter;
|
||||
|
||||
BOOL wasNewDevice;
|
||||
}
|
||||
@@ -127,12 +127,6 @@
|
||||
[_alertController dismissViewControllerAnimated:YES completion:nil];
|
||||
_alertController = nil;
|
||||
}
|
||||
|
||||
if (encryptionInfoView)
|
||||
{
|
||||
[encryptionInfoView removeFromSuperview];
|
||||
encryptionInfoView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,77 +136,43 @@
|
||||
UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController;
|
||||
if (rootViewController)
|
||||
{
|
||||
encryptionInfoView = [[EncryptionInfoView alloc] initWithDeviceInfo:_device andMatrixSession:_mxSession];
|
||||
[encryptionInfoView onButtonPressed:encryptionInfoView.verifyButton];
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:_mxSession];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
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];
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:rootViewController otherUserId:_device.userId otherDeviceId:_device.deviceId animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MXKEncryptionInfoViewDelegate
|
||||
#pragma mark - DeviceVerificationCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)encryptionInfoView:(MXKEncryptionInfoView *)theEncryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo *)deviceInfo
|
||||
- (void)deviceVerificationCoordinatorBridgePresenterDelegateDidComplete:(DeviceVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
encryptionInfoView = nil;
|
||||
[deviceVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
deviceVerificationCoordinatorBridgePresenter = nil;
|
||||
|
||||
if (deviceInfo.verified == MXDeviceVerified)
|
||||
{
|
||||
// Accept the received requests from this device
|
||||
// As the device is now verified, all other key requests will be automatically accepted.
|
||||
[self.mxSession.crypto acceptAllPendingKeyRequestsFromUser:self.device.userId andDevice:self.device.deviceId onComplete:^{
|
||||
// Check device new status
|
||||
[self.mxSession.crypto downloadKeys:@[self.device.userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap) {
|
||||
|
||||
onComplete();
|
||||
}];
|
||||
}
|
||||
}
|
||||
MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:self.device.deviceId forUser:self.device.userId];
|
||||
if (deviceInfo && deviceInfo.verified == MXDeviceVerified)
|
||||
{
|
||||
// Accept the received requests from this device
|
||||
// As the device is now verified, all other key requests will be automatically accepted.
|
||||
[self.mxSession.crypto acceptAllPendingKeyRequestsFromUser:self.device.userId andDevice:self.device.deviceId onComplete:^{
|
||||
|
||||
- (void)encryptionInfoViewDidClose:(MXKEncryptionInfoView *)theEncryptionInfoView
|
||||
{
|
||||
encryptionInfoView = nil;
|
||||
onComplete();
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Come back to self.alertController - ie, reopen it
|
||||
[self show];
|
||||
}
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
// Come back to self.alertController - ie, reopen it
|
||||
[self show];
|
||||
// Should not happen (the device is in the crypto db)
|
||||
[self show];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
if ([recentsDataSource.publicRoomsDirectoryDataSource.mxSession roomWithRoomId:publicRoom.roomId])
|
||||
{
|
||||
// Open the public room
|
||||
[[AppDelegate theDelegate].masterTabBarController selectRoomWithId:publicRoom.roomId andEventId:nil inMatrixSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
|
||||
[[AppDelegate theDelegate] showRoom:publicRoom.roomId andEventId:nil withMatrixSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession restoreInitialDisplay:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -31,6 +31,13 @@ import UIKit
|
||||
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error: Error)
|
||||
}
|
||||
|
||||
private enum BackupRows {
|
||||
case info(text: String)
|
||||
case createAction
|
||||
case restoreAction(keyBackupVersion: MXKeyBackupVersion, title: String)
|
||||
case deleteAction(keyBackupVersion: MXKeyBackupVersion)
|
||||
}
|
||||
|
||||
@objc final class SettingsKeyBackupTableViewSection: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
@@ -44,9 +51,15 @@ import UIKit
|
||||
private var viewModel: SettingsKeyBackupViewModelType!
|
||||
|
||||
// Need to know the state to make `cellForRow` deliver cells accordingly
|
||||
private var viewState: SettingsKeyBackupViewState = .checkingBackup
|
||||
private var viewState: SettingsKeyBackupViewState = .checkingBackup {
|
||||
didSet {
|
||||
self.updateBackupRows()
|
||||
}
|
||||
}
|
||||
|
||||
private var userDevice: MXDeviceInfo
|
||||
|
||||
private var backupRows: [BackupRows] = []
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@@ -58,53 +71,32 @@ import UIKit
|
||||
|
||||
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
|
||||
return self.backupRows.count
|
||||
}
|
||||
|
||||
|
||||
@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)
|
||||
guard let delegate = self.delegate else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
|
||||
let backupRow = self.backupRows[row]
|
||||
|
||||
var cell: UITableViewCell
|
||||
switch backupRow {
|
||||
case .info(let infoText):
|
||||
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
|
||||
infoCell.mxkTextView.text = infoText
|
||||
cell = infoCell
|
||||
case .createAction:
|
||||
cell = self.buttonCellForCreate(atRow: row)
|
||||
case .restoreAction(keyBackupVersion: let keyBackupVersion, let title):
|
||||
cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, title: title, atRow: row)
|
||||
case .deleteAction(keyBackupVersion: let keyBackupVersion):
|
||||
cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
@@ -116,236 +108,135 @@ import UIKit
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
var cell: UITableViewCell
|
||||
switch row {
|
||||
case 0:
|
||||
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
|
||||
|
||||
// MARK: - Data Computing
|
||||
|
||||
private func updateBackupRows() {
|
||||
|
||||
let backupRows: [BackupRows]
|
||||
|
||||
switch self.viewState {
|
||||
case .checkingBackup:
|
||||
|
||||
let info = VectorL10n.settingsKeyBackupInfo
|
||||
let checking = VectorL10n.settingsKeyBackupInfoChecking
|
||||
let strings = [info, "", checking]
|
||||
let text = strings.joined(separator: "\n")
|
||||
|
||||
backupRows = [
|
||||
.info(text: text)
|
||||
]
|
||||
|
||||
case .noBackup:
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
var cell: UITableViewCell
|
||||
switch row {
|
||||
case 0:
|
||||
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
|
||||
|
||||
let backupInfoText = strings.joined(separator: "\n")
|
||||
|
||||
backupRows = [
|
||||
.info(text: backupInfoText),
|
||||
.createAction
|
||||
]
|
||||
|
||||
case .backup(let keyBackupVersion, let keyBackupVersionTrust):
|
||||
|
||||
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 backupStrings = [info, "", backupStatus]
|
||||
let backupInfoText = backupStrings.joined(separator: "\n")
|
||||
|
||||
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 additionalStrings = [version, algorithm, uploadStatus]
|
||||
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
var cell: UITableViewCell
|
||||
switch row {
|
||||
case 0:
|
||||
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: 0)
|
||||
|
||||
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
||||
|
||||
var backupViewStateRows: [BackupRows] = [
|
||||
.info(text: backupInfoText),
|
||||
.info(text: additionalInfoText),
|
||||
.info(text: backupTrustInfoText)
|
||||
]
|
||||
|
||||
// TODO: Do not display restore button if all keys are stored on the device
|
||||
if true {
|
||||
backupViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonRestore))
|
||||
}
|
||||
|
||||
backupViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
||||
|
||||
backupRows = backupViewStateRows
|
||||
|
||||
case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress):
|
||||
|
||||
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 backupStrings = [info, "", backupStatus]
|
||||
let backupInfoText = backupStrings.joined(separator: "\n")
|
||||
|
||||
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 additionalStrings = [version, algorithm, uploadStatus]
|
||||
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
var cell: UITableViewCell
|
||||
switch row {
|
||||
case 0:
|
||||
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
|
||||
|
||||
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
||||
|
||||
var backupAndRunningViewStateRows: [BackupRows] = [
|
||||
.info(text: backupInfoText),
|
||||
.info(text: additionalInfoText),
|
||||
.info(text: backupTrustInfoText)
|
||||
]
|
||||
|
||||
// TODO: Do not display restore button if all keys are stored on the device
|
||||
if true {
|
||||
backupAndRunningViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonRestore))
|
||||
}
|
||||
|
||||
backupAndRunningViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
||||
|
||||
backupRows = backupAndRunningViewStateRows
|
||||
|
||||
case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust):
|
||||
|
||||
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 backupStrings = [info, "", backupStatus, "", signoutWarning]
|
||||
let backupInfoText = backupStrings.joined(separator: "\n")
|
||||
|
||||
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 additionalStrings = [version, algorithm]
|
||||
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
||||
|
||||
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()
|
||||
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
||||
|
||||
var backupNotTrustedViewStateRows: [BackupRows] = [
|
||||
.info(text: backupInfoText),
|
||||
.info(text: additionalInfoText),
|
||||
.info(text: backupTrustInfoText)
|
||||
]
|
||||
|
||||
// TODO: Do not display restore button if all keys are stored on the device
|
||||
if true {
|
||||
backupNotTrustedViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonConnect))
|
||||
}
|
||||
|
||||
backupNotTrustedViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
||||
|
||||
backupRows = backupNotTrustedViewStateRows
|
||||
}
|
||||
|
||||
return cell
|
||||
|
||||
self.backupRows = backupRows
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Data Computing
|
||||
|
||||
private func stringForKeyBackupTrust(_ keyBackupVersionTrust: MXKeyBackupVersionTrust) -> [String] {
|
||||
|
||||
return keyBackupVersionTrust.signatures.map { (signature) -> String in
|
||||
@@ -392,7 +283,7 @@ import UIKit
|
||||
return cell
|
||||
}
|
||||
|
||||
private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, atRow row: Int, title: String = VectorL10n.settingsKeyBackupButtonRestore) -> UITableViewCell {
|
||||
private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, title: String, atRow row: Int) -> UITableViewCell {
|
||||
guard let delegate = self.delegate else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
@@ -48,8 +48,6 @@
|
||||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
#import "EncryptionInfoView.h"
|
||||
|
||||
NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId";
|
||||
|
||||
enum
|
||||
@@ -622,12 +620,15 @@ SignOutAlertPresenterDelegate>
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
// Refresh the corresponding table view cell with animation
|
||||
[self.tableView reloadRowsAtIndexPaths:@[
|
||||
[NSIndexPath indexPathForRow:userSettingsNewEmailIndex inSection:SETTINGS_SECTION_USER_SETTINGS_INDEX]
|
||||
]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -648,12 +649,15 @@ SignOutAlertPresenterDelegate>
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
|
||||
// Refresh the corresponding table view cell with animation
|
||||
[self.tableView reloadRowsAtIndexPaths:@[
|
||||
[NSIndexPath indexPathForRow:userSettingsNewPhoneIndex inSection:SETTINGS_SECTION_USER_SETTINGS_INDEX]
|
||||
]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
|
||||
[self.tableView endUpdates];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4345,68 +4349,6 @@ SignOutAlertPresenterDelegate>
|
||||
[[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
|
||||
|
||||
@@ -816,15 +816,12 @@
|
||||
|
||||
self.tabBar.items[index].badgeValue = badgeValue;
|
||||
|
||||
if (@available(iOS 10, *))
|
||||
{
|
||||
self.tabBar.items[index].badgeColor = badgeColor;
|
||||
|
||||
[self.tabBar.items[index] setBadgeTextAttributes:@{
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.baseTextPrimaryColor
|
||||
}
|
||||
forState:UIControlStateNormal];
|
||||
}
|
||||
self.tabBar.items[index].badgeColor = badgeColor;
|
||||
|
||||
[self.tabBar.items[index] setBadgeTextAttributes:@{
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.baseTextPrimaryColor
|
||||
}
|
||||
forState:UIControlStateNormal];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -837,16 +834,13 @@
|
||||
if (mark)
|
||||
{
|
||||
self.tabBar.items[index].badgeValue = mark;
|
||||
|
||||
self.tabBar.items[index].badgeColor = badgeColor;
|
||||
|
||||
if (@available(iOS 10, *))
|
||||
{
|
||||
self.tabBar.items[index].badgeColor = badgeColor;
|
||||
|
||||
[self.tabBar.items[index] setBadgeTextAttributes:@{
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.baseTextPrimaryColor
|
||||
}
|
||||
forState:UIControlStateNormal];
|
||||
}
|
||||
[self.tabBar.items[index] setBadgeTextAttributes:@{
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.baseTextPrimaryColor
|
||||
}
|
||||
forState:UIControlStateNormal];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "DeviceTableViewCell.h"
|
||||
#import "EncryptionInfoView.h"
|
||||
|
||||
@interface UsersDevicesViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, DeviceTableViewCellDelegate, MXKEncryptionInfoViewDelegate>
|
||||
@interface UsersDevicesViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, DeviceTableViewCellDelegate>
|
||||
|
||||
/**
|
||||
Display a map of users/devices.
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#import "AppDelegate.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface UsersDevicesViewController ()
|
||||
@interface UsersDevicesViewController () <DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
MXUsersDevicesMap<MXDeviceInfo*> *usersDevices;
|
||||
MXSession *mxSession;
|
||||
|
||||
void (^onCompleteBlock)(BOOL doneButtonPressed);
|
||||
|
||||
DeviceVerificationCoordinatorBridgePresenter *deviceVerificationCoordinatorBridgePresenter;
|
||||
|
||||
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
||||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
@@ -214,51 +216,10 @@
|
||||
if (verificationStatus == MXDeviceVerified)
|
||||
{
|
||||
// Prompt the user before marking as verified the device.
|
||||
EncryptionInfoView *encryptionInfoView = [[EncryptionInfoView alloc] initWithDeviceInfo:deviceTableViewCell.deviceInfo andMatrixSession:mxSession];
|
||||
[encryptionInfoView onButtonPressed:encryptionInfoView.verifyButton];
|
||||
deviceVerificationCoordinatorBridgePresenter = [[DeviceVerificationCoordinatorBridgePresenter alloc] initWithSession:mxSession];
|
||||
deviceVerificationCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
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
|
||||
[self.view addSubview:encryptionInfoView];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.topLayoutGuide
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0f
|
||||
constant:10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.bottomLayoutGuide
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0f
|
||||
constant:-10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view
|
||||
attribute:NSLayoutAttributeLeading
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeLeading
|
||||
multiplier:1.0f
|
||||
constant:-10.0f]];
|
||||
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:encryptionInfoView
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1.0f
|
||||
constant:10.0f]];
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
[deviceVerificationCoordinatorBridgePresenter presentFrom:self otherUserId:deviceTableViewCell.deviceInfo.userId otherDeviceId:deviceTableViewCell.deviceInfo.deviceId animated:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -274,17 +235,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MXKEncryptionInfoViewDelegate
|
||||
#pragma mark - DeviceVerificationCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)encryptionInfoView:(MXKEncryptionInfoView *)encryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo *)deviceInfo
|
||||
- (void)deviceVerificationCoordinatorBridgePresenterDelegateDidComplete:(DeviceVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
||||
{
|
||||
// Update our map
|
||||
MXDeviceInfo *device = [usersDevices objectForDevice:deviceInfo.deviceId forUser:deviceInfo.userId];
|
||||
device.verified = deviceInfo.verified;
|
||||
[deviceVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
deviceVerificationCoordinatorBridgePresenter = nil;
|
||||
|
||||
[self.tableView reloadData];
|
||||
// Update our map
|
||||
MXWeakify(self);
|
||||
[mxSession.crypto downloadKeys:@[otherUserId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:otherDeviceId forUser:otherUserId];
|
||||
|
||||
MXDeviceInfo *device = [self->usersDevices objectForDevice:otherDeviceId forUser:otherUserId];
|
||||
device.verified = deviceInfo.verified;
|
||||
|
||||
[self.tableView reloadData];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
// Should not happen (the device is in the crypto db)
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - User actions
|
||||
|
||||
- (IBAction)onDone:(id)sender
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.8.4</string>
|
||||
<string>0.8.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.8.4</string>
|
||||
<string>0.8.5</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<true/>
|
||||
<key>ITSEncryptionExportComplianceCode</key>
|
||||
@@ -51,6 +51,8 @@
|
||||
<string>The photo library is used to send photos and videos.</string>
|
||||
<key>NSSiriUsageDescription</key>
|
||||
<string>Siri is used to perform calls even from the lock screen.</string>
|
||||
<key>NSCalendarsUsageDescription</key>
|
||||
<string>See your scheduled meetings in the app.</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
|
||||
@@ -10,3 +10,5 @@
|
||||
#import "ThemeService.h"
|
||||
#import "TableViewCellWithCheckBoxAndLabel.h"
|
||||
#import "RecentsDataSource.h"
|
||||
#import "AvatarGenerator.h"
|
||||
#import "EncryptionInfoView.h"
|
||||
|
||||
@@ -227,14 +227,7 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/";
|
||||
|
||||
self.defaultTextFont = [UIFont systemFontOfSize:15];
|
||||
self.prefixTextFont = [UIFont boldSystemFontOfSize:15];
|
||||
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
|
||||
{
|
||||
self.bingTextFont = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.bingTextFont = [UIFont systemFontOfSize:15];
|
||||
}
|
||||
self.bingTextFont = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
self.stateEventTextFont = [UIFont italicSystemFontOfSize:15];
|
||||
self.callNoticesTextFont = [UIFont italicSystemFontOfSize:15];
|
||||
self.encryptedMessagesTextFont = [UIFont italicSystemFontOfSize:15];
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 267 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty 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 <JitsiMeet/JitsiMeetView.h>
|
||||
#import <JitsiMeet/JitsiMeetViewDelegate.h>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user