Finish v0.10.0

This commit is contained in:
manuroe 2019-10-11 15:56:55 +02:00
commit 25aa432a79
98 changed files with 8338 additions and 999 deletions

View file

@ -1,3 +1,36 @@
Changes in 0.10.0 (2019-10-11)
===============================================
Improvements:
* Upgrade MatrixKit version ([v0.11.1](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.11.1)).
* Upgrade MatrixKit version ([v0.11.0](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.11.0)).
* Widgets: Whitelist [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961) widget urls.
* Settings: CALLS section: Always display the CallKit option but grey it out when not available (only on China).
* VoIP: Fallback to matrix.org STUN server with a confirmation dialog (#2646).
* Widgets: Whitelist [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961) widget urls
* i18n: Enable Polish (pl).
* Room members: third-party invites can now be revoked
* Privacy: Prompt to accept integration manager policies on use (#2600).
* Privacy: Make clear that device names are publicly readable (#2662).
* Privacy: Remove the ability to set an IS at login/registration (#2661).
* Privacy: Remove the bind true flag from 3PID calls on registration (#2648).
* Privacy: Remove the bind true flag from 3PID adds in settings (#2650).
* Privacy: Email help text on registration should be updated without binding (#2675).
* Privacy: Use MXIdentityService to perform identity server requests (#2647).
* Privacy: Support identity server v2 API authentication (#2603).
* Privacy: Use the hashed v2 lookup API for 3PIDs (#2652).
* Privacy: Prompt to accept identity server policies on firt use (#2602).
* Privacy: Settings: Allow adding 3pids when no IS (#2659).
* Privacy: Allow password reset when no IS (#2658).
* Privacy: Allow email registration when no IS (#2657).
* Privacy: Settings: Add a Discovery section (#2606).
* Privacy: Make NSContactsUsageDescription more generic and mention that 3pids are now uploaded hashed (#2521).
* Privacy: Settings: Add IDENTITY SERVER section (#2604).
* Privacy: Make IS terms wording clearer when we fallback to vector.im (#2760).
Bug fix:
* Theme: Make button theming work (#2734).
Changes in 0.9.5 (2019-09-20)
===============================================
@ -30,6 +63,7 @@ Improvements:
* Widgets: Whitelist https://scalar-staging.vector.im/api (#2612).
* Reactions: Show who reacted (#2591).
* Media picking: Use native camera and use separate actions for camera and media picker (#638).
* Ability to disable all identity server functionality via the config file (#2643).
Bug fix:
* Crash when leaving settings due to backup section refresh animation.

View file

@ -7,7 +7,7 @@ use_frameworks!
# Different flavours of pods to MatrixKit
# The current MatrixKit pod version
$matrixKitVersion = '0.10.2'
$matrixKitVersion = '0.11.1'
# The develop branch version
#$matrixKitVersion = 'develop'

View file

@ -49,41 +49,41 @@ PODS:
- MatomoTracker (6.0.1):
- MatomoTracker/Core (= 6.0.1)
- MatomoTracker/Core (6.0.1)
- MatrixKit (0.10.2):
- MatrixKit (0.11.1):
- cmark (~> 0.24.1)
- DTCoreText (~> 1.6.21)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixKit/Core (= 0.10.2)
- MatrixSDK (= 0.13.1)
- MatrixKit/Core (= 0.11.1)
- MatrixSDK (= 0.14.0)
- SwiftUTI (~> 1.0.6)
- MatrixKit/AppExtension (0.10.2):
- MatrixKit/AppExtension (0.11.1):
- cmark (~> 0.24.1)
- DTCoreText (~> 1.6.21)
- DTCoreText/Extension
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.13.1)
- MatrixSDK (= 0.14.0)
- SwiftUTI (~> 1.0.6)
- MatrixKit/Core (0.10.2):
- MatrixKit/Core (0.11.1):
- cmark (~> 0.24.1)
- DTCoreText (~> 1.6.21)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.13.1)
- MatrixSDK (= 0.14.0)
- SwiftUTI (~> 1.0.6)
- MatrixSDK (0.13.1):
- MatrixSDK/Core (= 0.13.1)
- MatrixSDK/Core (0.13.1):
- MatrixSDK (0.14.0):
- MatrixSDK/Core (= 0.14.0)
- MatrixSDK/Core (0.14.0):
- AFNetworking (~> 3.2.0)
- GZIP (~> 1.2.2)
- libbase58 (~> 0.1.4)
- OLMKit (~> 3.1.0)
- Realm (~> 3.13.1)
- MatrixSDK/JingleCallStack (0.13.1):
- MatrixSDK/JingleCallStack (0.14.0):
- JitsiMeetSDK (~> 2.1.0)
- MatrixSDK/Core
- MatrixSDK/SwiftSupport (0.13.1):
- MatrixSDK/SwiftSupport (0.14.0):
- MatrixSDK/Core
- OLMKit (3.1.0):
- OLMKit/olmc (= 3.1.0)
@ -109,8 +109,8 @@ DEPENDENCIES:
- DTCoreText
- GBDeviceInfo (~> 5.2.0)
- MatomoTracker (~> 6.0.1)
- MatrixKit (= 0.10.2)
- MatrixKit/AppExtension (= 0.10.2)
- MatrixKit (= 0.11.1)
- MatrixKit/AppExtension (= 0.11.1)
- MatrixSDK/JingleCallStack
- MatrixSDK/SwiftSupport
- OLMKit
@ -121,7 +121,7 @@ DEPENDENCIES:
- zxcvbn-ios
SPEC REPOS:
https://github.com/cocoapods/specs.git:
trunk:
- AFNetworking
- cmark
- DGCollectionViewLeftAlignFlowLayout
@ -150,7 +150,7 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS:
SwiftUTI:
:commit: c21237f13e9fb31a07f3fcd5243c5cf79d75901c
:commit: b6b46942fb3aad819610851f62a70e17a528444e
:git: https://github.com/speramusinc/SwiftUTI.git
SPEC CHECKSUMS:
@ -166,8 +166,8 @@ SPEC CHECKSUMS:
libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5
MatrixKit: 208801ba995442a6daa913eb6d639bb918c91c24
MatrixSDK: 16c8c4b530a7f9fb264baced52b6b0948b1d0a98
MatrixKit: db698352271a738b3c0e2f1a331088ea41f90ae1
MatrixSDK: 0f965f8700420696ab15bf48d827a03d98b0ffac
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
Realm: 50071da38fe079e0735e47c9f2eae738c68c5996
Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b
@ -176,6 +176,6 @@ SPEC CHECKSUMS:
SwiftUTI: 917993c124f8eac25e88ced0202fc58d7eb50fa8
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
PODFILE CHECKSUM: 131195037301d9bfb7465da86b3ea79aac686b74
PODFILE CHECKSUM: 724a8f79621df730d939fff2b7e703f5c9ec3b06
COCOAPODS: 1.7.2
COCOAPODS: 1.8.3

View file

@ -96,6 +96,17 @@
32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; };
32BF995521FA2AB700698084 /* SettingsKeyBackupViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995421FA2AB700698084 /* SettingsKeyBackupViewAction.swift */; };
32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995621FB07A400698084 /* SettingsKeyBackupTableViewSection.swift */; };
32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */; };
32DB557622FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */; };
32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */; };
32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */; };
32DB557922FDADE50016329E /* ServiceTermsModalScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */; };
32DB557A22FDADE50016329E /* ServiceTermsModalScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */; };
32DB557B22FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */; };
32DB557C22FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */; };
32DB557D22FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */; };
32DB557E22FDADE50016329E /* ServiceTermsModalScreenViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */; };
32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */; };
32F6B9692270623100BBA352 /* DeviceVerificationDataLoadingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9632270623000BBA352 /* DeviceVerificationDataLoadingCoordinator.swift */; };
32F6B96A2270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9642270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift */; };
32F6B96B2270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9652270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift */; };
@ -147,6 +158,11 @@
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */; };
B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872221F098F0003554A5 /* ActivityIndicatorView.swift */; };
B120863722EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B120863622EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift */; };
B125FE1B231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE1A231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift */; };
B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE1C231D5DE400B72806 /* SettingsDiscoveryViewModel.swift */; };
B125FE1F231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE1E231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift */; };
B125FE21231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE20231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift */; };
B125FE23231D5E4300B72806 /* SettingsDiscoveryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B125FE22231D5E4300B72806 /* SettingsDiscoveryViewState.swift */; };
B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */; };
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */; };
B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */; };
@ -168,6 +184,15 @@
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */; };
B152C73122DF561E0041315A /* EmojiServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B152C73022DF561E0041315A /* EmojiServiceTests.swift */; };
B152C73522DF599C0041315A /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = B152C73422DF599B0041315A /* apple_emojis_data.json */; };
B157FA9F23264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9723264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift */; };
B157FAA023264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9823264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift */; };
B157FAA123264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9923264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorType.swift */; };
B157FAA223264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9A23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift */; };
B157FAA323264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9B23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift */; };
B157FAA423264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B157FA9C23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.storyboard */; };
B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9D23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift */; };
B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FA9E23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift */; };
B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157FAA723264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift */; };
B1664BC520F4E67600808783 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1664BAD20F4E67500808783 /* FallbackViewController.xib */; };
B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BAE20F4E67500808783 /* FallbackViewController.m */; };
B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BB220F4E67500808783 /* SharePresentingViewController.m */; };
@ -481,6 +506,15 @@
B1C3360222F1ED600021BA8D /* MediaPickerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */; };
B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C3360022F1ED600021BA8D /* MediaPickerCoordinator.swift */; };
B1C3361C22F32B4A0021BA8D /* SingleImagePickerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C3361B22F32B4A0021BA8D /* SingleImagePickerPresenter.swift */; };
B1C45A84232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A7B232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift */; };
B1C45A85232A8C2600165425 /* SettingsIdentityServerViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1C45A7C232A8C2600165425 /* SettingsIdentityServerViewController.storyboard */; };
B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A7D232A8C2600165425 /* SettingsIdentityServerViewModelType.swift */; };
B1C45A87232A8C2600165425 /* SettingsIdentityServerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A7E232A8C2600165425 /* SettingsIdentityServerCoordinator.swift */; };
B1C45A88232A8C2600165425 /* SettingsIdentityServerViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A7F232A8C2600165425 /* SettingsIdentityServerViewState.swift */; };
B1C45A89232A8C2600165425 /* SettingsIdentityServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A80232A8C2600165425 /* SettingsIdentityServerViewController.swift */; };
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A81232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift */; };
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A82232A8C2600165425 /* SettingsIdentityServerViewModel.swift */; };
B1C45A8C232A8C2600165425 /* SettingsIdentityServerViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C45A83232A8C2600165425 /* SettingsIdentityServerViewAction.swift */; };
B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */; };
B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562CB228AB3510037F12A /* UIStackView.swift */; };
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */; };
@ -704,6 +738,20 @@
32D7159E2146CC6F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Vector.strings; sourceTree = "<group>"; };
32D7159F2146CC7F00DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
32D715A02146CC8800DF59C9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
32DAF8DB231813C800654A44 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
32DAF8DC231813D500654A44 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
32DAF8DD231813E100654A44 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Vector.strings; sourceTree = "<group>"; };
32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinatorType.swift; sourceTree = "<group>"; };
32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalCoordinator.swift; sourceTree = "<group>"; };
32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewState.swift; sourceTree = "<group>"; };
32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewModel.swift; sourceTree = "<group>"; };
32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewController.swift; sourceTree = "<group>"; };
32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ServiceTermsModalScreenViewController.storyboard; sourceTree = "<group>"; };
32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewModelType.swift; sourceTree = "<group>"; };
32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenCoordinatorType.swift; sourceTree = "<group>"; };
32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenViewAction.swift; sourceTree = "<group>"; };
32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTermsModalScreenCoordinator.swift; sourceTree = "<group>"; };
32F6B9632270623000BBA352 /* DeviceVerificationDataLoadingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingCoordinator.swift; sourceTree = "<group>"; };
32F6B9642270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewState.swift; sourceTree = "<group>"; };
32F6B9652270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewAction.swift; sourceTree = "<group>"; };
@ -768,6 +816,11 @@
B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorPresenter.swift; sourceTree = "<group>"; };
B110872221F098F0003554A5 /* ActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorView.swift; sourceTree = "<group>"; };
B120863622EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionHistoryBridgeCoordinatorPresenter.swift; sourceTree = "<group>"; };
B125FE1A231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryTableViewSection.swift; sourceTree = "<group>"; };
B125FE1C231D5DE400B72806 /* SettingsDiscoveryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewModel.swift; sourceTree = "<group>"; };
B125FE1E231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewModelType.swift; sourceTree = "<group>"; };
B125FE20231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewAction.swift; sourceTree = "<group>"; };
B125FE22231D5E4300B72806 /* SettingsDiscoveryViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryViewState.swift; sourceTree = "<group>"; };
B139C21A21FE5B9100BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModel.swift; sourceTree = "<group>"; };
B139C21C21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewModelType.swift; sourceTree = "<group>"; };
B139C21E21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewAction.swift; sourceTree = "<group>"; };
@ -790,6 +843,15 @@
B152C72D22DF55D80041315A /* RiotTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RiotTests-Bridging-Header.h"; sourceTree = "<group>"; };
B152C73022DF561E0041315A /* EmojiServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiServiceTests.swift; sourceTree = "<group>"; };
B152C73422DF599B0041315A /* apple_emojis_data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
B157FA9723264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsCoordinator.swift; sourceTree = "<group>"; };
B157FA9823264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewModelType.swift; sourceTree = "<group>"; };
B157FA9923264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsCoordinatorType.swift; sourceTree = "<group>"; };
B157FA9A23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewAction.swift; sourceTree = "<group>"; };
B157FA9B23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewState.swift; sourceTree = "<group>"; };
B157FA9C23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SettingsDiscoveryThreePidDetailsViewController.storyboard; sourceTree = "<group>"; };
B157FA9D23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewModel.swift; sourceTree = "<group>"; };
B157FA9E23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsViewController.swift; sourceTree = "<group>"; };
B157FAA723264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
B1664BAD20F4E67500808783 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = "<group>"; };
B1664BAE20F4E67500808783 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = "<group>"; };
B1664BAF20F4E67500808783 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = "<group>"; };
@ -1287,6 +1349,15 @@
B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
B1C3360022F1ED600021BA8D /* MediaPickerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinator.swift; sourceTree = "<group>"; };
B1C3361B22F32B4A0021BA8D /* SingleImagePickerPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleImagePickerPresenter.swift; sourceTree = "<group>"; };
B1C45A7B232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerCoordinatorType.swift; sourceTree = "<group>"; };
B1C45A7C232A8C2600165425 /* SettingsIdentityServerViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SettingsIdentityServerViewController.storyboard; sourceTree = "<group>"; };
B1C45A7D232A8C2600165425 /* SettingsIdentityServerViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewModelType.swift; sourceTree = "<group>"; };
B1C45A7E232A8C2600165425 /* SettingsIdentityServerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerCoordinator.swift; sourceTree = "<group>"; };
B1C45A7F232A8C2600165425 /* SettingsIdentityServerViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewState.swift; sourceTree = "<group>"; };
B1C45A80232A8C2600165425 /* SettingsIdentityServerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewController.swift; sourceTree = "<group>"; };
B1C45A81232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
B1C45A82232A8C2600165425 /* SettingsIdentityServerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewModel.swift; sourceTree = "<group>"; };
B1C45A83232A8C2600165425 /* SettingsIdentityServerViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsIdentityServerViewAction.swift; sourceTree = "<group>"; };
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = "<group>"; };
B1C562CB228AB3510037F12A /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuItem.swift; sourceTree = "<group>"; };
@ -1701,6 +1772,40 @@
path = KeyBackup;
sourceTree = "<group>";
};
32DB556722FDADE50016329E /* ServiceTerms */ = {
isa = PBXGroup;
children = (
32DB556822FDADE50016329E /* Modal */,
);
path = ServiceTerms;
sourceTree = "<group>";
};
32DB556822FDADE50016329E /* Modal */ = {
isa = PBXGroup;
children = (
32DB556922FDADE50016329E /* ServiceTermsModalCoordinatorType.swift */,
32DB556A22FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift */,
32DB556B22FDADE50016329E /* ServiceTermsModalCoordinator.swift */,
32DB556C22FDADE50016329E /* Modal */,
);
path = Modal;
sourceTree = "<group>";
};
32DB556C22FDADE50016329E /* Modal */ = {
isa = PBXGroup;
children = (
32DB556D22FDADE50016329E /* ServiceTermsModalScreenViewState.swift */,
32DB556E22FDADE50016329E /* ServiceTermsModalScreenViewModel.swift */,
32DB556F22FDADE50016329E /* ServiceTermsModalScreenViewController.swift */,
32DB557022FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard */,
32DB557122FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift */,
32DB557222FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift */,
32DB557322FDADE50016329E /* ServiceTermsModalScreenViewAction.swift */,
32DB557422FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift */,
);
path = Modal;
sourceTree = "<group>";
};
4220F60B660591FD80AF3428 /* Pods */ = {
isa = PBXGroup;
children = (
@ -1859,6 +1964,19 @@
path = ActivityIndicator;
sourceTree = "<group>";
};
B125FE19231D5B5600B72806 /* Discovery */ = {
isa = PBXGroup;
children = (
B125FE1A231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift */,
B125FE1E231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift */,
B125FE1C231D5DE400B72806 /* SettingsDiscoveryViewModel.swift */,
B125FE20231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift */,
B125FE22231D5E4300B72806 /* SettingsDiscoveryViewState.swift */,
B157FA9623264AE800EBFBD4 /* ThreePidDetails */,
);
path = Discovery;
sourceTree = "<group>";
};
B14F142522144F6400FA0595 /* RecoveryKey */ = {
isa = PBXGroup;
children = (
@ -1907,6 +2025,22 @@
path = Data;
sourceTree = "<group>";
};
B157FA9623264AE800EBFBD4 /* ThreePidDetails */ = {
isa = PBXGroup;
children = (
B157FAA723264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift */,
B157FA9923264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorType.swift */,
B157FA9723264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift */,
B157FA9823264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift */,
B157FA9D23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift */,
B157FA9A23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift */,
B157FA9B23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift */,
B157FA9E23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift */,
B157FA9C23264AE800EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.storyboard */,
);
path = ThreePidDetails;
sourceTree = "<group>";
};
B1664BAB20F4E67500808783 /* Modules */ = {
isa = PBXGroup;
children = (
@ -2190,6 +2324,7 @@
B1B5567620EE6C4C00210D55 /* Modules */ = {
isa = PBXGroup;
children = (
32DB556722FDADE50016329E /* ServiceTerms */,
3232AB94225730E100AD6A5C /* DeviceVerification */,
B1B556EA20EE6C4C00210D55 /* Main */,
B1B556CA20EE6C4C00210D55 /* TabBar */,
@ -2227,6 +2362,8 @@
B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */,
B1B5578120EF564900210D55 /* Views */,
32BF994D21FA1C6300698084 /* KeyBackup */,
B125FE19231D5B5600B72806 /* Discovery */,
B1C45A7A232A8C2600165425 /* IdentityServer */,
B1B5567B20EE6C4C00210D55 /* Language */,
B1B5567820EE6C4C00210D55 /* PhoneCountry */,
B1B5568020EE6C4C00210D55 /* DeactivateAccount */,
@ -3342,6 +3479,22 @@
path = Camera;
sourceTree = "<group>";
};
B1C45A7A232A8C2600165425 /* IdentityServer */ = {
isa = PBXGroup;
children = (
B1C45A81232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift */,
B1C45A7B232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift */,
B1C45A7E232A8C2600165425 /* SettingsIdentityServerCoordinator.swift */,
B1C45A7D232A8C2600165425 /* SettingsIdentityServerViewModelType.swift */,
B1C45A82232A8C2600165425 /* SettingsIdentityServerViewModel.swift */,
B1C45A83232A8C2600165425 /* SettingsIdentityServerViewAction.swift */,
B1C45A7F232A8C2600165425 /* SettingsIdentityServerViewState.swift */,
B1C45A80232A8C2600165425 /* SettingsIdentityServerViewController.swift */,
B1C45A7C232A8C2600165425 /* SettingsIdentityServerViewController.storyboard */,
);
path = IdentityServer;
sourceTree = "<group>";
};
B1C562D7228C0B4C0037F12A /* ContextualMenu */ = {
isa = PBXGroup;
children = (
@ -3759,6 +3912,7 @@
es,
ja,
hu,
pl,
);
mainGroup = F094A9991B78D8F000B1FBBF;
productRefGroup = F094A9A31B78D8F000B1FBBF /* Products */;
@ -3814,6 +3968,7 @@
B1B5597020EFA85D00210D55 /* EncryptionInfoView.xib in Resources */,
B1B558BC20EF768F00210D55 /* RoomMembershipBubbleCell.xib in Resources */,
B1B5571F20EE6C4D00210D55 /* ContactDetailsViewController.xib in Resources */,
B157FAA423264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.storyboard in Resources */,
B1B558E220EF768F00210D55 /* RoomIncomingTextMsgBubbleCell.xib in Resources */,
B105778B221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard in Resources */,
B1664DA220F4F95800808783 /* Localizable.strings in Resources */,
@ -3845,7 +4000,9 @@
B1B558F620EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.xib in Resources */,
B1B5574D20EE6C4D00210D55 /* MediaPickerViewController.xib in Resources */,
B1B5575020EE6C4D00210D55 /* AuthenticationViewController.xib in Resources */,
B1C45A85232A8C2600165425 /* SettingsIdentityServerViewController.storyboard in Resources */,
B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */,
32DB557B22FDADE50016329E /* ServiceTermsModalScreenViewController.storyboard in Resources */,
B1B5574320EE6C4D00210D55 /* CallViewController.xib in Resources */,
F083BDEA1E7009ED00A9B29C /* ringback.mp3 in Resources */,
F083BDF21E7009ED00A9B29C /* GoogleService-Info.plist in Resources */,
@ -4155,6 +4312,7 @@
324A2053225FC571004FE8B0 /* DeviceVerificationIncomingViewModel.swift in Sources */,
B1B557A120EF58AD00210D55 /* ContactTableViewCell.m in Sources */,
F083BE021E7009ED00A9B29C /* AvatarGenerator.m in Sources */,
B157FAA023264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift in Sources */,
B1B5573A20EE6C4D00210D55 /* GroupRoomsViewController.m in Sources */,
B1B558F920EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m in Sources */,
B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */,
@ -4165,6 +4323,7 @@
B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */,
B1B9DEDE22E9D9890065E677 /* EmojiServiceType.swift in Sources */,
3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */,
32DB557C22FDADE50016329E /* ServiceTermsModalScreenViewModelType.swift in Sources */,
B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */,
B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */,
B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */,
@ -4172,6 +4331,7 @@
B1B5574B20EE6C4D00210D55 /* MediaAlbumContentViewController.m in Sources */,
B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */,
B1DB4F0E22316FFF0065DBFA /* UserNameColorGenerator.swift in Sources */,
B157FAA123264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorType.swift in Sources */,
B1057789221304EC00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift in Sources */,
B1DCC61922E5E17100625807 /* EmojiPickerCoordinatorType.swift in Sources */,
B1C3360122F1ED600021BA8D /* MediaPickerCoordinatorType.swift in Sources */,
@ -4216,6 +4376,7 @@
B1C3361C22F32B4A0021BA8D /* SingleImagePickerPresenter.swift in Sources */,
B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */,
B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */,
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */,
@ -4231,6 +4392,7 @@
B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */,
B1DCC63122E7026F00625807 /* EmojiPickerHeaderView.swift in Sources */,
B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */,
B1C45A8C232A8C2600165425 /* SettingsIdentityServerViewAction.swift in Sources */,
32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */,
B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */,
B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */,
@ -4239,11 +4401,14 @@
B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */,
32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */,
B1C45A88232A8C2600165425 /* SettingsIdentityServerViewState.swift in Sources */,
B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */,
32DB557822FDADE50016329E /* ServiceTermsModalScreenViewState.swift in Sources */,
B1B558E120EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.m in Sources */,
B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */,
B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */,
32F6B9692270623100BBA352 /* DeviceVerificationDataLoadingCoordinator.swift in Sources */,
B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */,
B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */,
B10CFBC32268D99D00A5842E /* JitsiService.swift in Sources */,
B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */,
@ -4260,6 +4425,7 @@
3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */,
B1B9DEE822EB34EF0065E677 /* ReactionHistoryCoordinatorType.swift in Sources */,
B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */,
32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */,
B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */,
B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */,
3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */,
@ -4267,6 +4433,8 @@
3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */,
F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */,
B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */,
32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */,
32DB557922FDADE50016329E /* ServiceTermsModalScreenViewModel.swift in Sources */,
32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */,
32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */,
F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */,
@ -4277,6 +4445,7 @@
B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */,
3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */,
3232AB4E2256558300AD6A5C /* TemplateScreenViewModelType.swift in Sources */,
32DB557D22FDADE50016329E /* ServiceTermsModalScreenCoordinatorType.swift in Sources */,
B1B5590520EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
3232ABA5225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift in Sources */,
B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */,
@ -4287,6 +4456,7 @@
B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */,
B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */,
B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */,
32DB557A22FDADE50016329E /* ServiceTermsModalScreenViewController.swift in Sources */,
B1B9DEF422EB426D0065E677 /* ReactionHistoryViewCell.swift in Sources */,
B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */,
B1DCC61B22E5E17100625807 /* EmojiPickerCoordinator.swift in Sources */,
@ -4299,6 +4469,7 @@
B1B5582C20EF666100210D55 /* DirectoryRecentTableViewCell.m in Sources */,
B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */,
B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */,
B125FE21231D5E1D00B72806 /* SettingsDiscoveryViewAction.swift in Sources */,
3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */,
B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */,
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */,
@ -4310,7 +4481,11 @@
B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */,
B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */,
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */,
B157FA9F23264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift in Sources */,
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */,
B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */,
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */,
B1DCC62422E60CA900625807 /* EmojiPickerCategoryViewData.swift in Sources */,
324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */,
B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */,
@ -4343,12 +4518,16 @@
B1B12B2922942315002CB419 /* UITouch.swift in Sources */,
B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */,
B1C45A84232A8C2600165425 /* SettingsIdentityServerCoordinatorType.swift in Sources */,
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */,
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */,
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */,
B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */,
B1B5574020EE6C4D00210D55 /* SegmentedViewController.m in Sources */,
B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */,
B125FE1F231D5DF700B72806 /* SettingsDiscoveryViewModelType.swift in Sources */,
B157FAA323264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift in Sources */,
B1098BF921ECFE65000DDA48 /* KeyBackupSetupCoordinator.swift in Sources */,
B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */,
B1098BFE21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift in Sources */,
@ -4382,6 +4561,7 @@
B1B557BE20EF5B4500210D55 /* RoomInputToolbarView.m in Sources */,
32A6001922C661100042C1D9 /* EditHistoryViewModelType.swift in Sources */,
B1B5573B20EE6C4D00210D55 /* FavouritesViewController.m in Sources */,
B1C45A87232A8C2600165425 /* SettingsIdentityServerCoordinator.swift in Sources */,
B1B5579920EF575B00210D55 /* AuthInputsView.m in Sources */,
B1B5597520EFB02A00210D55 /* InviteRecentTableViewCell.m in Sources */,
B1B5571E20EE6C4D00210D55 /* ContactDetailsViewController.m in Sources */,
@ -4404,6 +4584,7 @@
B1B557D820EF5EA900210D55 /* RoomActivitiesView.m in Sources */,
B1B9DEE922EB34EF0065E677 /* ReactionHistoryViewController.swift in Sources */,
B1B5596620EF9E9B00210D55 /* RoomTableViewCell.m in Sources */,
B1C45A89232A8C2600165425 /* SettingsIdentityServerViewController.swift in Sources */,
B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */,
32A6001822C661100042C1D9 /* EditHistoryViewModel.swift in Sources */,
B1B558D020EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
@ -4423,6 +4604,7 @@
B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */,
B1B558F020EF768F00210D55 /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */,
926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */,
B125FE23231D5E4300B72806 /* SettingsDiscoveryViewState.swift in Sources */,
B1B5593820EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.m in Sources */,
B1DCC62222E60BE000625807 /* EmojiPickerItemViewData.swift in Sources */,
3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */,
@ -4439,6 +4621,7 @@
B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */,
B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */,
B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */,
B157FAA223264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewAction.swift in Sources */,
3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */,
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */,
323AB947232BD74600C1451F /* AuthFallBackViewController.m in Sources */,
@ -4461,6 +4644,7 @@
B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */,
B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */,
B1B5575C20EE6C4D00210D55 /* DirectoryViewController.m in Sources */,
32DB557622FDADE50016329E /* ServiceTermsModalCoordinatorBridgePresenter.swift in Sources */,
B1B558BD20EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */,
B1B5577020EE702800210D55 /* WidgetPickerViewController.m in Sources */,
B1B558D320EF768F00210D55 /* RoomOutgoingEncryptedTextMsgBubbleCell.m in Sources */,
@ -4476,6 +4660,7 @@
32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */,
B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */,
F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */,
32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */,
F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */,
B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */,
324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */,
@ -4484,6 +4669,7 @@
3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */,
32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */,
B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */,
32DB557E22FDADE50016329E /* ServiceTermsModalScreenViewAction.swift in Sources */,
32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */,
B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */,
B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */,
@ -4497,6 +4683,7 @@
3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */,
B1B9DEF122EB396B0065E677 /* ReactionHistoryViewData.swift in Sources */,
B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
B125FE1B231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift in Sources */,
32242F0921E8B05F00725742 /* UIColor.swift in Sources */,
B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */,
B1B558CE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.m in Sources */,
@ -4510,6 +4697,7 @@
B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */,
32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */,
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */,
B157FAA523264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModel.swift in Sources */,
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */,
B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */,
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */,
@ -4567,6 +4755,7 @@
32D7159E2146CC6F00DF59C9 /* es */,
3D78489221AC9E6500B98A7D /* ja */,
3D78489521ACA25300B98A7D /* hu */,
32DAF8DD231813E100654A44 /* pl */,
);
name = Vector.strings;
sourceTree = "<group>";
@ -4589,6 +4778,7 @@
32D715A02146CC8800DF59C9 /* es */,
3D78489021AC9E6400B98A7D /* ja */,
3D78489321ACA25200B98A7D /* hu */,
32DAF8DB231813C800654A44 /* pl */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
@ -4611,6 +4801,7 @@
32D7159F2146CC7F00DF59C9 /* es */,
3D78489121AC9E6500B98A7D /* ja */,
3D78489421ACA25300B98A7D /* hu */,
32DAF8DC231813D500654A44 /* pl */,
);
name = Localizable.strings;
sourceTree = "<group>";

View file

@ -38,6 +38,10 @@ extern NSString *const kAppDelegateDidTapStatusBarNotification;
*/
extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
extern NSString *const AppDelegateDidValidateEmailNotification;
extern NSString *const AppDelegateDidValidateEmailNotificationSIDKey;
extern NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey;
@interface AppDelegate : UIResponder <UIApplicationDelegate, MXKCallViewControllerDelegate, UISplitViewControllerDelegate, UINavigationControllerDelegate, JitsiViewControllerDelegate, UNUserNotificationCenterDelegate>
{
BOOL isPushRegistered;

View file

@ -84,7 +84,11 @@
NSString *const kAppDelegateDidTapStatusBarNotification = @"kAppDelegateDidTapStatusBarNotification";
NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateNetworkStatusDidChangeNotification";
@interface AppDelegate () <PKPushRegistryDelegate, GDPRConsentViewControllerDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
NSString *const AppDelegateDidValidateEmailNotification = @"AppDelegateDidValidateEmailNotification";
NSString *const AppDelegateDidValidateEmailNotificationSIDKey = @"AppDelegateDidValidateEmailNotificationSIDKey";
NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDelegateDidValidateEmailNotificationClientSecretKey";
@interface AppDelegate () <PKPushRegistryDelegate, GDPRConsentViewControllerDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate, ServiceTermsModalCoordinatorBridgePresenterDelegate>
{
/**
Reachability observer
@ -233,6 +237,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
@property (weak, nonatomic) UIAlertController *gdprConsentNotGivenAlertController;
@property (weak, nonatomic) UIViewController *gdprConsentController;
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
/**
Used to manage on boarding steps, like create DM with riot bot
*/
@ -647,6 +653,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Register to GDPR consent not given notification
[self registerUserConsentNotGivenNotification];
// Register to identity server terms not signed notification
[self registerIdentityServiceTermsNotSignedNotification];
// Start monitoring reachability
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
@ -692,9 +701,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[account resume];
}
// Refresh local contact from the contact book.
[self refreshLocalContacts];
_isAppForeground = YES;
if (@available(iOS 11.0, *))
@ -2106,19 +2112,40 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
return [self handleServerProvionningLink:webURL];
}
NSString *validateEmailSubmitTokenPath = @"validate/email/submitToken";
NSString *validateEmailSubmitTokenAPIPathV1 = [NSString stringWithFormat:@"/%@/%@", kMXIdentityAPIPrefixPathV1, validateEmailSubmitTokenPath];
NSString *validateEmailSubmitTokenAPIPathV2 = [NSString stringWithFormat:@"/%@/%@", kMXIdentityAPIPrefixPathV2, validateEmailSubmitTokenPath];
// Manage email validation link
if ([webURL.path isEqualToString:@"/_matrix/identity/api/v1/validate/email/submitToken"])
if ([webURL.path isEqualToString:validateEmailSubmitTokenAPIPathV1] || [webURL.path isEqualToString:validateEmailSubmitTokenAPIPathV2])
{
// Validate the email on the passed identity server
NSString *identityServer = [NSString stringWithFormat:@"%@://%@", webURL.scheme, webURL.host];
MXRestClient *identityRestClient = [[MXRestClient alloc] initWithHomeServer:identityServer andOnUnrecognizedCertificateBlock:nil];
MXSession *mainSession = self.mxSessions.firstObject;
MXRestClient *homeserverRestClient;
if (mainSession.matrixRestClient)
{
homeserverRestClient = mainSession.matrixRestClient;
}
else
{
homeserverRestClient = [[MXRestClient alloc] initWithHomeServer:identityServer andOnUnrecognizedCertificateBlock:nil];
}
MXIdentityService *identityService = [[MXIdentityService alloc] initWithIdentityServer:identityServer accessToken:nil andHomeserverRestClient:homeserverRestClient];
// Extract required parameters from the link
NSArray<NSString*> *pathParams;
NSMutableDictionary *queryParams;
[self parseUniversalLinkFragment:webURL.absoluteString outPathParams:&pathParams outQueryParams:&queryParams];
[identityRestClient submit3PIDValidationToken:queryParams[@"token"] medium:kMX3PIDMediumEmail clientSecret:queryParams[@"client_secret"] sid:queryParams[@"sid"] success:^{
NSString *clientSecret = queryParams[@"client_secret"];
NSString *sid = queryParams[@"sid"];
[identityService submit3PIDValidationToken:queryParams[@"token"] medium:kMX3PIDMediumEmail clientSecret:clientSecret sid:sid success:^{
NSLog(@"[AppDelegate] handleUniversalLink. Email successfully validated.");
@ -2132,7 +2159,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
else
{
// No nextLink in Vector world means validation for binding a new email
NSLog(@"[AppDelegate] handleUniversalLink. TODO: Complete email binding");
// Post a notification about email validation to make a chance to SettingsDiscoveryThreePidDetailsViewModel to make it discoverable or not by the identity server.
if (clientSecret && sid)
{
NSDictionary *userInfo = @{ AppDelegateDidValidateEmailNotificationClientSecretKey : clientSecret,
AppDelegateDidValidateEmailNotificationSIDKey : sid };
[[NSNotificationCenter defaultCenter] postNotificationName:AppDelegateDidValidateEmailNotification object:nil userInfo:userInfo];
}
}
} failure:^(NSError *error) {
@ -2697,6 +2732,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Let's call invite be valid for 1 minute
mxSession.callManager.inviteLifetime = 60000;
if (RiotSettings.shared.allowStunServerFallback)
{
mxSession.callManager.fallbackSTUNServer = RiotSettings.shared.stunServerFallback;
}
// Setup CallKit
if ([MXCallKitAdapter callKitAvailable])
{
@ -2972,6 +3012,14 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// during this blocking task.
dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[[MXKContactManager sharedManager] addMatrixSession:mxSession];
// Load the local contacts on first account
if ([MXKAccountManager sharedManager].accounts.count == 1)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshLocalContacts];
});
}
});
// Update home data sources
@ -3869,8 +3917,21 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
- (void)refreshLocalContacts
{
// Do not scan local contacts in background if the user has not decided yet about using
// an identity server
BOOL doRefreshLocalContacts = NO;
for (MXSession *session in mxSessionArray)
{
if (session.hasAccountDataIdentityServerValue)
{
doRefreshLocalContacts = YES;
break;
}
}
// Check whether the application is allowed to access the local contacts.
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
if (doRefreshLocalContacts
&& [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
{
// Check the user permission for syncing local contacts. This permission was handled independently on previous application version.
if (![MXKAppSettings standardAppSettings].syncLocalContacts)
@ -3943,6 +4004,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[self addCallStatusBar:btnTitle];
}
if ([callViewController isKindOfClass:[CallViewController class]]
&& ((CallViewController*)callViewController).shouldPromptForStunServerFallback)
{
[self promptForStunServerFallback];
}
if (completion)
{
completion();
@ -3975,6 +4042,52 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}
}
- (void)promptForStunServerFallback
{
[_errorNotification dismissViewControllerAnimated:NO completion:nil];
NSString *stunFallbackHost = RiotSettings.shared.stunServerFallback;
// Remove "stun:"
stunFallbackHost = [stunFallbackHost componentsSeparatedByString:@":"].lastObject;
MXSession *mainSession = self.mxSessions.firstObject;
NSString *homeServerName = mainSession.matrixRestClient.credentials.homeServerName;
NSString *message = [NSString stringWithFormat:@"%@\n\n%@",
[NSString stringWithFormat:NSLocalizedStringFromTable(@"call_no_stun_server_error_message_1", @"Vector", nil), homeServerName],
[NSString stringWithFormat: NSLocalizedStringFromTable(@"call_no_stun_server_error_message_2", @"Vector", nil), stunFallbackHost]];
_errorNotification = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"call_no_stun_server_error_title", @"Vector", nil)
message:message
preferredStyle:UIAlertControllerStyleAlert];
[_errorNotification addAction:[UIAlertAction actionWithTitle:[NSString stringWithFormat: NSLocalizedStringFromTable(@"call_no_stun_server_error_use_fallback_button", @"Vector", nil), stunFallbackHost]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
RiotSettings.shared.allowStunServerFallback = YES;
mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.stunServerFallback;
[AppDelegate theDelegate].errorNotification = nil;
}]];
[_errorNotification addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
RiotSettings.shared.allowStunServerFallback = NO;
[AppDelegate theDelegate].errorNotification = nil;
}]];
// Display the error notification
if (!isErrorNotificationSuspended)
{
[_errorNotification mxk_setAccessibilityIdentifier:@"AppDelegateErrorAlert"];
[self showNotificationAlert:_errorNotification];
}
}
#pragma mark - Jitsi call
- (void)displayJitsiViewControllerWithWidget:(Widget*)jitsiWidget andVideo:(BOOL)video
@ -4718,6 +4831,84 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}];
}
#pragma mark - Identity server service terms
// Observe identity server terms not signed notification
- (void)registerIdentityServiceTermsNotSignedNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleIdentityServiceTermsNotSignedNotification:) name:MXIdentityServiceTermsNotSignedNotification object:nil];
}
- (void)handleIdentityServiceTermsNotSignedNotification:(NSNotification*)notification
{
NSLog(@"[AppDelegate] IS Terms: handleIdentityServiceTermsNotSignedNotification.");
NSString *baseURL;
NSString *accessToken;
MXJSONModelSetString(baseURL, notification.userInfo[MXIdentityServiceNotificationIdentityServerKey]);
MXJSONModelSetString(accessToken, notification.userInfo[MXIdentityServiceNotificationAccessTokenKey]);
[self presentIdentityServerTermsWithBaseURL:baseURL andAccessToken:accessToken];
}
- (void)presentIdentityServerTermsWithBaseURL:(NSString*)baseURL andAccessToken:(NSString*)accessToken
{
MXSession *mxSession = self.mxSessions.firstObject;
if (!mxSession || !baseURL || !accessToken || self.serviceTermsModalCoordinatorBridgePresenter.isPresenting)
{
return;
}
ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter = [[ServiceTermsModalCoordinatorBridgePresenter alloc] initWithSession:mxSession
baseUrl:baseURL
serviceType:MXServiceTypeIdentityService
outOfContext:YES
accessToken:accessToken];
serviceTermsModalCoordinatorBridgePresenter.delegate = self;
UIViewController *presentingViewController = self.window.rootViewController.presentedViewController ?: self.window.rootViewController;
[serviceTermsModalCoordinatorBridgePresenter presentFrom:presentingViewController animated:YES];
self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline:(ServiceTermsModalCoordinatorBridgePresenter *)coordinatorBridgePresenter session:(MXSession *)session
{
NSLog(@"[AppDelegate] IS Terms: User has declined the use of the default IS.");
// The user does not want to use the proposed IS.
// Disable IS feature on user's account
[session setIdentityServer:nil andAccessToken:nil];
[session setAccountDataIdentityServer:nil success:^{
} failure:^(NSError *error) {
NSLog(@"[AppDelegate] IS Terms: Error: %@", error);
}];
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
#pragma mark - Settings
- (void)setupUserDefaults

View file

@ -32,9 +32,11 @@
<string>https://jitsi.riot.im</string>
<key>integrationsWidgetsUrls</key>
<array>
<string>https://scalar-staging.riot.im/scalar/api</string>
<string>https://scalar-staging.vector.im/api</string>
<string>https://scalar.vector.im/_matrix/integrations/v1</string>
<string>https://scalar.vector.im/api</string>
<string>https://scalar-staging.vector.im/_matrix/integrations/v1</string>
<string>https://scalar-staging.vector.im/api</string>
<string>https://scalar-staging.riot.im/scalar/api</string>
</array>
<key>piwik</key>
<dict>
@ -57,6 +59,8 @@
<false/>
<key>createConferenceCallsWithJitsi</key>
<true/>
<key>stunServerFallback</key>
<string>stun:turn.matrix.org</string>
<key>enableRageShake</key>
<true/>
<key>maxAllowedMediaCacheSize</key>

View file

@ -820,3 +820,29 @@
"image_picker_action_library" = "Избери от библиотеката";
"camera_unavailable" = "Не е достъпна камера на вашето устройство";
"photo_library_access_not_granted" = "%@ няма достъп до библиотеката със снимки. Моля, променете настройките на поверителността";
"auth_forgot_password_error_no_configured_identity_server" = "Не е конфигуриран сървър за самоличност: добавете такъв за да може да възстановите паролата си.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Не е конфигуриран сървър за самоличност, така че не можете да поканите участник по имейл адрес.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Не е конфигуриран сървър за самоличност, така че не можете да започнете чат с контакт посредством имейл адрес.";
// Service terms
"service_terms_modal_title" = "Условия за ползване";
"service_terms_modal_message" = "За да продължите е необходимо да приемете Условията за ползване.";
"service_terms_modal_accept_button" = "Приемам";
"service_terms_modal_description_for_identity_server" = "Бъдете откриваеми от потребители";
"service_terms_modal_description_for_integration_manager" = "Използвайте ботове, връзки към други мрежи и стикери";
"room_participants_remove_third_party_invite_prompt_msg" = "Сигурни ли сте, че искате да оттеглите тази покана?";
// Errors
"error_user_already_logged_in" = "Изглежда се опитвате да се свържете с друг сървър. Искате ли да излезе от профила?";
"room_accessiblity_scroll_to_bottom" = "Отиди най-долу";
"room_accessibility_search" = "Търси";
"room_accessibility_integrations" = "Интеграции";
"room_accessibility_upload" = "Качи";
"room_accessibility_call" = "Обади се";
"room_accessibility_hangup" = "Затвори";
"media_type_accessibility_image" = "Снимка";
"media_type_accessibility_audio" = "Аудио";
"media_type_accessibility_video" = "Видео";
"media_type_accessibility_location" = "Местоположение";
"media_type_accessibility_file" = "Файл";
"media_type_accessibility_sticker" = "Стикер";
// Widget Picker
"widget_picker_title" = "Интеграции";

View file

@ -1,6 +1,6 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "Die Kamera wird verwendet um Fotos und Videos aufzunehmen sowie Video-Anrufe zu führen.";
"NSPhotoLibraryUsageDescription" = "Die Foto-Bibliothek wird verwendet um Fotos und Videos zu senden.";
"NSMicrophoneUsageDescription" = "Das Mikrofon wird verwendet um Videos aufzunehmen sowie Gespräche zu führen.";
"NSContactsUsageDescription" = "Um dir zu zeigen, welche deiner Kontakte bereits Riot oder Matrix benutzen, können wir die E-Mail-Adressen und Telefonnummern deines Adressbuches an deinen Matrix-Identitätsserver senden. New Vector speichert diese Daten nicht und nutzt sie auch für keine anderen Zwecke. Für mehr Informationen sieh dir bitte die Datenschutz-Seite in den App-Einstellungen an.";
"NSCameraUsageDescription" = "Die Kamera wird verwendet, um Fotos und Videos aufzunehmen sowie Videoanrufe durchzuführen.";
"NSPhotoLibraryUsageDescription" = "Die Fotobibliothek wird verwendet, um Fotos und Videos zu versenden.";
"NSMicrophoneUsageDescription" = "Das Mikrofon wird verwendet, um Videos aufzunehmen sowie Gespräche zu führen.";
"NSContactsUsageDescription" = "Riot kann E-Mail-Adressen und Telefonnummern aus deinem Adressbuch zu deinem Matrix-Identitätsserver schicken, um Kontakte zu finden, die bereits Matrix verwenden. Wenn verfügbar, werden persönliche Daten vor dem Versand gehasht. Für weitere Informationen schaue bitte in die Datenschutzerklärung deines Identitätsservers.";
"NSCalendarsUsageDescription" = "Sieh dir deine geplanten Meetings in der App an.";

View file

@ -243,8 +243,8 @@
"settings_new_password" = "neues Passwort";
"settings_confirm_password" = "bestätige Passwort";
"settings_password_updated" = "Dein Passwort wurde aktualisiert";
"settings_crypto_device_name" = "Gerätename: ";
"settings_crypto_device_key" = "\nGeräte-Schlüssel: ";
"settings_crypto_device_name" = "Öffentlicher Gerätename: ";
"settings_crypto_device_key" = "\nGeräteschlüssel:\n";
"settings_crypto_export" = "Schlüssel exportieren";
"room_details_settings" = "Einstellungen";
"room_details_photo" = "Raum-Bild";
@ -309,7 +309,7 @@
"room_event_action_redact" = "Entfernen";
"room_warning_about_encryption" = "Ende-zu-Ende Verschlüsselung ist in Beta und ist evtl. nicht zuverlässig.\n\nMan sollte noch nicht darauf vertrauen, dass die Daten sicher sind.\n\nGeräte werden den Verlauf vor dem Beitritt des Raumes nicht entschlüsseln können.\n\nVerschlüsselte Nachrichten sind nicht lesbar auf Clients, die Verschlüsselung noch nicht implementiert haben.";
"unknown_devices_alert" = "Dieser Raum enthält unbekannte Geräte, die nicht verifiziert wurden.\nDas bedeutet, es gibt keine Garantie, dass sie dem User gehören, der angegeben ist.\nWir empfehlen eine Überprüfung für jedes Gerät, bevor du weitermachst, aber du kannst die Nachricht erneut senden ohne Verifikation.";
"room_preview_unlinked_email_warning" = "Diese Einladung wurde an %@ gesendet, die Adresse ist nicht mit diesem Konto verbunden. Du kannst dich mit einem anderen Konto anmelden, oder diese E-Mail-Adresse deinem Konto hinzufügen.";
"room_preview_unlinked_email_warning" = "Diese Einladung wurde an %@ gesendet, das diesem Konto nicht zugeordnet ist. Möglicherweise möchten Sie sich mit einem anderen Konto anmelden oder diese E-Mail zu Ihrem Konto hinzufügen.";
"room_preview_try_join_an_unknown_room" = "Du versuchst auf %@ zuzugreifen. Möchtest du dem Raum beitreten um teilzunehmen?";
"settings_config_identity_server" = "Identitätsserver ist %@";
"settings_labs" = "Labor";
@ -415,7 +415,7 @@
"contacts_user_directory_section" = "NUTZER VERZEICHNIS";
"contacts_user_directory_offline_section" = "NUTZER VERZEICHNIS (offline)";
"auth_home_server_placeholder" = "URL (z.B. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (z.B. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (z. B. https://vector.im)";
"room_ongoing_conference_call_close" = "Schließen";
"room_conference_call_no_power" = "Du brauchst die Berechtigung Konferenzgespräche in diesem Raum zu verwalten";
"settings_labs_create_conference_with_jitsi" = "Erstelle Konferenzgespräche mit Jitsi";
@ -511,8 +511,8 @@
"room_do_not_have_permission_to_post" = "Du hast keine Berechtigung in diesem Raum zu posten";
"room_event_action_kick_prompt_reason" = "Grund für den Rauswurf des Benutzers";
"room_event_action_ban_prompt_reason" = "Grund für die Verbannung des Benutzers";
"room_action_send_photo_or_video" = "Sende Foto oder Video";
"room_action_send_sticker" = "Sende Sticker";
"room_action_send_photo_or_video" = "Foto oder Video senden";
"room_action_send_sticker" = "Sticker senden";
"settings_deactivate_account" = "DEAKTIVIERE ACCOUNT";
"settings_deactivate_my_account" = "Deaktivere meinen Account";
"widget_sticker_picker_no_stickerpacks_alert" = "Du hast aktuell keine Sticker-Packete aktiviert.";
@ -544,7 +544,7 @@
"room_replacement_link" = "Die Konversation wird hier fortgesetzt.";
"room_predecessor_information" = "Dieser Raum ist die Fortsetzung einer anderen Konversation.";
"room_predecessor_link" = "Tippe hier um ältere Nachrichten zu sehen.";
"settings_labs_room_members_lazy_loading" = "Raummitglieder nachladen";
"settings_labs_room_members_lazy_loading" = "Raummitglieder später laden";
"settings_labs_room_members_lazy_loading_error_message" = "Dein Heimserver unterstützt das verzögerte Laden von Raummitgliedern noch nicht. Versuche es später erneut.";
"room_event_action_view_decrypted_source" = "Zeige entschlüsselten Quellcode";
"room_recents_server_notice_section" = "SYSTEMBENACHRICHTIGUNGEN";
@ -561,11 +561,11 @@
"settings_key_backup_info" = "Verschlüsselte Nachrichten sind durch Ende-zu-Ende-Verschlüsselung gesichert. Ausschließlich du und der/die Empfänger besitzen die Schlüssel, um diese Nachrichten zu lesen.";
"settings_key_backup_info_checking" = "Überprüfe...";
"settings_key_backup_info_none" = "Deine Schlüssel werden von diesem Gerät nicht gesichert.";
"settings_key_backup_info_signout_warning" = "Sichere deine Schlüssel bevor du dich abmeldest, damit du sie nicht verlierst.";
"settings_key_backup_info_signout_warning" = "Schließen Sie dieses Gerät an die Schlüsselsicherung an, bevor Sie sich abmelden, um den Verlust von Schlüsseln zu vermeiden, die sich möglicherweise nur auf diesem Gerät befinden.";
"settings_key_backup_info_version" = "Schlüssel Sicherheitskopie Version: %@";
"settings_key_backup_info_algorithm" = "Algorithmus";
"settings_key_backup_info_valid" = "Dieses Gerät sichert deine Schlüssel.";
"settings_key_backup_info_not_valid" = "Dieses Gerät sichert deine Schlüssel nicht.";
"settings_key_backup_info_not_valid" = "Dieses Gerät sichert Ihre Schlüssel nicht, es ist jedoch eine Sicherungskopie vorhanden, die Sie wiederherstellen und später hinzufügen können.";
"settings_key_backup_info_progress" = "Sichere %@ Schlüssel...";
"settings_key_backup_info_progress_done" = "Alle Schlüssel wurden gesichert";
"settings_key_backup_info_trust_signature_unknown" = "Sicherheitskopie hat eine Signatur vom Gerät mit der ID: %@";
@ -674,7 +674,7 @@
"key_backup_recover_connent_banner_subtitle" = "Schlüssel dieses Geräts sichern";
// MARK: - Device Verification
"device_verification_title" = "Gerät verifizieren";
"device_verification_cancelled" = "Die Gegenseite hat die Verifikation abgebrochen";
"device_verification_cancelled" = "Die Gegenstelle hat die Überprüfung abgebrochen.";
"device_verification_cancelled_by_me" = "Die Verifikation wurde abgebrochen. Grund: %@";
"device_verification_error_cannot_load_device" = "Konnte Geräteinformationen nicht laden.";
// Mark: Incoming
@ -736,7 +736,7 @@
"device_verification_emoji_bell" = "Glocke";
"device_verification_emoji_anchor" = "Anker";
"device_verification_emoji_headphones" = "Kopfhörer";
"close" = "Schließen";
"close" = "Beenden";
"auth_softlogout_signed_out" = "Sie sind abgemeldet";
"auth_softlogout_sign_in" = "Einloggen";
"auth_softlogout_reason" = "Ihr Homeserver-Administrator (%1$@) hat Sie von Ihrem Konto %2$@ (%3$@) abgemeldet.";
@ -775,7 +775,7 @@
"device_verification_emoji_rooster" = "Hahn";
"device_verification_emoji_globe" = "Globus";
"device_verification_emoji_smiley" = "Lächeln";
"device_verification_emoji_spanner" = "Spanner";
"device_verification_emoji_spanner" = "Schraubenschlüssel";
"device_verification_emoji_thumbs up" = "Daumen hoch";
"device_verification_emoji_hourglass" = "Sanduhr";
"device_verification_emoji_clock" = "Uhr";
@ -798,3 +798,20 @@
"emoji_picker_flags_category" = "Flaggen";
// MARK: Reaction history
"reaction_history_title" = "Reaktionen";
"room_action_camera" = "Foto oder Video aufnehmen";
// Media picker
"media_picker_title" = "Mediathek";
// Image picker
"image_picker_action_camera" = "Foto aufnehmen";
"image_picker_action_library" = "Aus der Mediathek auswählen";
"camera_unavailable" = "Die Kamera ist auf Ihrem Gerät nicht verfügbar";
"photo_library_access_not_granted" = "%@ hat keine Berechtigung zum Zugriff auf die Fotobibliothek. Bitte ändern Sie die Datenschutzeinstellungen";
"auth_forgot_password_error_no_configured_identity_server" = "Es ist kein Identitätsserver konfiguriert: Fügen Sie einen hinzu, um Ihr Kennwort zurückzusetzen.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Es ist kein Identitätsserver konfiguriert, sodass Sie keinen Teilnehmer mit einer E-Mail hinzufügen können.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Es ist kein Identitätsserver konfiguriert, sodass Sie keinen Chat mit einem Kontakt über eine E-Mail starten können.";
// Service terms
"service_terms_modal_title" = "Nutzungsbedingungen";
"service_terms_modal_message" = "Um fortzufahren, müssen Sie die Bedingungen dieses Dienstes akzeptieren.";
"service_terms_modal_accept_button" = "Akzeptieren";
"service_terms_modal_description_for_identity_server" = "Für andere auffindbar sein";
"service_terms_modal_description_for_integration_manager" = "Verwenden Sie Bots, Bridges, Widgets und Sticker-Packs";

View file

@ -18,6 +18,6 @@
"NSCameraUsageDescription" = "The camera is used to take photos and videos, make video calls.";
"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.";
"NSContactsUsageDescription" = "To discover contacts already using Matrix, Riot can send email addresses and phone numbers in your address book to your chosen Matrix identity server. Where supported, personal data is hashed before sending - please check your identity server's privacy policy for more details.";
"NSCalendarsUsageDescription" = "See your scheduled meetings in the app.";

View file

@ -77,26 +77,28 @@
"auth_repeat_password_placeholder" = "Repeat password";
"auth_repeat_new_password_placeholder" = "Confirm your new password";
"auth_home_server_placeholder" = "URL (e.g. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (e.g. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (e.g. https://vector.im)";
"auth_invalid_login_param" = "Incorrect username and/or password";
"auth_invalid_user_name" = "User names may only contain letters, numbers, dots, hyphens and underscores";
"auth_invalid_password" = "Password too short (min 6)";
"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 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.";
"auth_add_email_message_2" = "Set an email for account recovery, and later to be optionally discoverable by people who know you.";
"auth_add_phone_message_2" = "Set a phone, and later to be optionally discoverable by people who know you.";
"auth_add_email_phone_message_2" = "Set an email for account recovery. Use later email or phone to be optionally discoverable by people who know you.";
"auth_missing_email" = "Missing email address";
"auth_missing_phone" = "Missing phone number";
"auth_missing_email_or_phone" = "Missing email address or phone number";
"auth_email_in_use" = "This email address is already in use";
"auth_phone_in_use" = "This phone number is already in use";
"auth_email_is_required" = "No identity server is configured so you cannot add an email address in order to reset your password in the future.";
"auth_phone_is_required" = "No identity server is configured so you cannot add a phone number in order to reset your password in the future.";
"auth_untrusted_id_server" = "The identity server is not trusted";
"auth_password_dont_match" = "Passwords don't match";
"auth_username_in_use" = "Username in use";
"auth_forgot_password" = "Forgot password?";
"auth_forgot_password_error_no_configured_identity_server" = "No identity server is configured: add one to reset your password.";
"auth_email_not_found" = "Failed to send email: This email address was not found";
"auth_use_server_options" = "Use custom server options (advanced)";
"auth_email_validation_message" = "Please check your email to continue registration";
@ -111,6 +113,7 @@
"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_is_required" = "No identity server is configured: add one in server options to reset your password.";
"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:";
@ -146,6 +149,7 @@
"room_creation_make_private" = "Make private";
"room_creation_wait_for_creation" = "A room is already being created. Please wait.";
"room_creation_invite_another_user" = "Search / invite by User ID, Name or email";
"room_creation_error_invite_user_by_email_without_identity_server" = "No identity server is configured so you cannot add a participant with an email.";
// Room recents
"room_recents_directory_section" = "ROOM DIRECTORY";
@ -197,6 +201,7 @@
// Contacts
"contacts_address_book_section" = "LOCAL CONTACTS";
"contacts_address_book_matrix_users_toggle" = "Matrix users only";
"contacts_address_book_no_identity_server" = "No identity server configured";
"contacts_address_book_no_contact" = "No local contacts";
"contacts_address_book_permission_required" = "Permission required to access local contacts";
"contacts_address_book_permission_denied" = "You didn't allow Riot to access your local contacts";
@ -212,7 +217,7 @@
"room_participants_leave_prompt_msg" = "Are you sure you want to leave the room?";
"room_participants_remove_prompt_title" = "Confirmation";
"room_participants_remove_prompt_msg" = "Are you sure you want to remove %@ from this chat?";
"room_participants_remove_third_party_invite_msg" = "Remove third-party invite is not supported yet until the api exists";
"room_participants_remove_third_party_invite_prompt_msg" = "Are you sure you want to revoke this invite?";
"room_participants_invite_prompt_title" = "Confirmation";
"room_participants_invite_prompt_msg" = "Are you sure you want to invite %@ to this chat?";
"room_participants_filter_room_members" = "Filter room members";
@ -220,6 +225,7 @@
"room_participants_invite_malformed_id_title" = "Invite Error";
"room_participants_invite_malformed_id" = "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'";
"room_participants_invited_section" = "INVITED";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "No identity server is configured so you cannot start a chat with a contact using an email.";
"room_participants_online" = "Online";
"room_participants_offline" = "Offline";
@ -370,6 +376,8 @@
"settings_user_settings" = "USER SETTINGS";
"settings_notifications_settings" = "NOTIFICATION SETTINGS";
"settings_calls_settings" = "CALLS";
"settings_discovery_settings" = "DISCOVERY";
"settings_identity_server_settings" = "IDENTITY SERVER";
"settings_user_interface" = "USER INTERFACE";
"settings_ignored_users" = "IGNORED USERS";
"settings_contacts" = "LOCAL CONTACTS";
@ -400,6 +408,9 @@
"settings_change_password" = "Change password";
"settings_night_mode" = "Night Mode";
"settings_fail_to_update_profile" = "Fail to update profile";
"settings_three_pids_management_information_part1" = "Manage which email addresses or phone numbers you can use to log in or recover your account here. Control who can find you in ";
"settings_three_pids_management_information_part2" = "Discovery";
"settings_three_pids_management_information_part3" = ".";
"settings_enable_push_notif" = "Notifications on this device";
"settings_show_decrypted_content" = "Show decrypted content";
@ -417,6 +428,9 @@
"settings_enable_callkit" = "Integrated calling";
"settings_callkit_info" = "Receive incoming calls on your lock screen. See your Riot calls in the system's call history. If iCloud is enabled, this call history will be shared with Apple.";
"settings_calls_stun_server_fallback_button" = "Allow fallback call assist server";
"settings_calls_stun_server_fallback_description" = "Allow fallback call assist server %@ when your homeserver does not offer one (your IP address would be shared during a call).";
"settings_ui_language" = "Language";
"settings_ui_theme" = "Theme";
"settings_ui_theme_auto" = "Auto";
@ -458,9 +472,9 @@
"settings_fail_to_update_password" = "Fail to update password";
"settings_password_updated" = "Your password has been updated";
"settings_crypto_device_name" = "Device name: ";
"settings_crypto_device_name" = "Device Public Name: ";
"settings_crypto_device_id" = "\nDevice ID: ";
"settings_crypto_device_key" = "\nDevice key: ";
"settings_crypto_device_key" = "\nDevice key:\n";
"settings_crypto_export" = "Export keys";
"settings_crypto_blacklist_unverified_devices" = "Encrypt to verified devices only";
@ -491,6 +505,60 @@
"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.";
"settings_devices_description" = "A device's public name is visible to people you communicate with";
"settings_discovery_no_identity_server" = "You are not currently using an identity server. To be discoverable by existing contacts you known, add one.";
"settings_discovery_terms_not_signed" = "Agree to the Identity Server (%@) Terms of Service to allow yourself to be discoverable by email address or phone number.";
"settings_discovery_three_pids_management_information_part1" = "Manage which email addresses or phone numbers other users can use to discover you and use to invite you to rooms. Add or remove email addresses or phone numbers from this list in ";
"settings_discovery_three_pids_management_information_part2" = "User Settings";
"settings_discovery_three_pids_management_information_part3" = ".";
"settings_discovery_error_message" = "An error occured. Please retry.";
"settings_discovery_three_pid_details_title_email" = "Manage email";
"settings_discovery_three_pid_details_information_email" = "Manage preferences for this email address, which other users can use to discover you and use to invite you to rooms. Add or remove email addresses in Accounts.";
"settings_discovery_three_pid_details_title_phone_number" = "Manage phone number";
"settings_discovery_three_pid_details_information_phone_number" = "Manage preferences for this phone number, which other users can use to discover you and use to invite you to rooms. Add or remove phone numbers in Accounts.";
"settings_discovery_three_pid_details_share_action" = "Share";
"settings_discovery_three_pid_details_revoke_action" = "Revoke";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "Cancel email validation";
"settings_discovery_three_pid_details_enter_sms_code_action" = "Enter SMS activation code";
"settings_identity_server_description" = "Using the identity server set above, you can discover and be discoverable by existing contacts you know.";
"settings_identity_server_no_is" = "No identity server configured";
"settings_identity_server_no_is_description" = "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one above.";
// Identity server settings
"identity_server_settings_title" = "Identity Server";
"identity_server_settings_description" = "You are currently using %@ to discover and be discoverable by existing contacts you know.";
"identity_server_settings_no_is_description" = "You are not currently using an identity server. To discover and be discoverable by existing contacts, add one above.";
"identity_server_settings_place_holder" = "Enter an identity server";
"identity_server_settings_add" = "Add";
"identity_server_settings_change" = "Change";
"identity_server_settings_disconnect_info" = "Disconnecting from your identity server will mean you wont be discoverable by other users and be able to invite others by email or phone.";
"identity_server_settings_disconnect" = "Disconnect";
"identity_server_settings_alert_no_terms_title" = "Identity server has no terms of services";
"identity_server_settings_alert_no_terms" = "The identity server you have chosen does not have any terms of service. Only continue if you trust the owner of the server.";
"identity_server_settings_alert_change_title" = "Change identity server";
"identity_server_settings_alert_change" = "Disconnect from the identity server %1$@ and connect to %2$@ instead?";
"identity_server_settings_alert_disconnect_title" = "Disconnect identity server";
"identity_server_settings_alert_disconnect" = "Disconnect from the identity server %@?";
"identity_server_settings_alert_disconnect_button" = "Disconnect";
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "You are still sharing your personal data on the identity server %@.\n\nWe recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.";
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Disconnect anyway";
"identity_server_settings_alert_error_terms_not_accepted" = "You must accept terms of %@ to set it as identity server.";
"identity_server_settings_alert_error_invalid_identity_server" = "%@ is not a valid identity server.";
// Room Details
"room_details_title" = "Room Details";
"room_details_people" = "Members";
@ -641,6 +709,11 @@
"call_already_displayed" = "There is already a call in progress.";
"call_jitsi_error" = "Failed to join the conference call.";
"call_no_stun_server_error_title" ="Call failed due to misconfigured server";
"call_no_stun_server_error_message_1" ="Please ask the administrator of your homeserver %@ to configure a TURN server in order for calls to work reliably.";
"call_no_stun_server_error_message_2" ="Alternatively, you can try to use the public server at %@, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings";
"call_no_stun_server_error_use_fallback_button" = "Try using %@";
// No VoIP support
"no_voip_title" = "Incoming call";
"no_voip" = "%@ is calling you but %@ does not support calls yet.\nYou can ignore this notification and answer the call from another device or you can reject it.";
@ -709,6 +782,21 @@
"gdpr_consent_not_given_alert_message" = "To continue using the %@ homeserver you must review and agree to the terms and conditions.";
"gdpr_consent_not_given_alert_review_now_action" = "Review now";
// Service terms
"service_terms_modal_title" = "Terms Of Service";
"service_terms_modal_message" = "To continue you need to accept the terms of this service (%@).";
"service_terms_modal_accept_button" = "Accept";
"service_terms_modal_decline_button" = "Decline";
"service_terms_modal_description_for_identity_server_1" = "Find others by phone or email";
"service_terms_modal_description_for_identity_server_2" = "Be found by phone or email";
"service_terms_modal_description_for_integration_manager" = "Use Bots, bridges, widgets and sticker packs";
// Service terms - Variant for identity server when displayed out of a context
"service_terms_modal_title_identity_server" = "Contact discovery";
"service_terms_modal_message_identity_server" = "Accept the terms of the identity server (%@) to discover contacts.";
// Deactivate account
"deactivate_account_title" = "Deactivate Account";
@ -958,3 +1046,7 @@
// MARK: Reaction history
"reaction_history_title" = "Reactions";
// Generic errors
"error_invite_3pid_with_no_identity_server" = "Add an identity server in your settings to invite by email.";

View file

@ -2,5 +2,5 @@
"NSCameraUsageDescription" = "Kamera argazkiak eta bideoak ateratzeko eta bideo deiak egiteko erabiltzen da.";
"NSPhotoLibraryUsageDescription" = "Argazkien liburutegia argazkiak eta bideoak bidaltzeko erabiltzen da.";
"NSMicrophoneUsageDescription" = "Mikrofonoa bideoak atera eta deiak egiteko erabiltzen da.";
"NSContactsUsageDescription" = "Zure kontaktuetatik Riot edo Matrix jada darabiltenak erakusteko, zure kontaktu liburuko e-mail helbideak eta telefono zenbakiak bidali ditzakegu identitate zerbitzarira. New Vector enpresak ez ditu datu hauek gordetzen edo beste ezertarako erabiltzen. Informazio gehiagorako ikusi pribatutasun politikari buruzko orria aplikazioaren ezarpenetan.";
"NSContactsUsageDescription" = "Zure kontaktuetatik Riot edo Matrix jada darabiltenak erakusteko, zure kontaktu liburuko e-mail helbideak eta telefono zenbakiak bidali ditzakegu identitate-zerbitzarira. New Vector enpresak ez ditu datu hauek gordetzen edo beste ezertarako erabiltzen. Informazio gehiagorako ikusi pribatutasun politikari buruzko orria aplikazioaren ezarpenetan.";
"NSCalendarsUsageDescription" = "Ikusi zure programatutako batzarrak aplikazioan.";

View file

@ -798,3 +798,23 @@
"emoji_picker_objects_category" = "Objektuak";
"emoji_picker_symbols_category" = "Sinboloak";
"emoji_picker_flags_category" = "Banderak";
"auth_forgot_password_error_no_configured_identity_server" = "Ez da identitate zerbitzaririk konfiguratu: gehitu bat zure pasahitza berrezartzeko.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Ez da identitate zerbitzaririk konfiguratu, beraz ezin duzu parte-hartzaile bat e-mail bidez gehitu.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Ez da identitate zerbitzaririk konfiguratu, beraz ezin duzu kontaktu batekin txat bat hasi e-mail bat erabiliz.";
"room_event_action_reaction_history" = "Erreakzioen historiala";
"room_action_camera" = "Atera argazkia edo bideoa";
// Media picker
"media_picker_title" = "Multimedia liburutegia";
// Image picker
"image_picker_action_camera" = "Atera argazkia";
"image_picker_action_library" = "Hautatu liburutegitik";
"camera_unavailable" = "Kamera ez dago erabilgarri zure gailuan";
"photo_library_access_not_granted" = "%@(e)k ez du argazki liburutegia erabiltzeko baimenik, aldatu pribatutasun ezarpenak";
// Service terms
"service_terms_modal_title" = "Erabilera baldintzak";
"service_terms_modal_message" = "Jarraitzeko, zerbitzu honen erabilera baldintzak onartu behar dituzu.";
"service_terms_modal_accept_button" = "Onartu";
"service_terms_modal_description_for_identity_server" = "Izan besteentzat aurkigarria";
"service_terms_modal_description_for_integration_manager" = "Erabili botak, zubiak, trepetak eta eranskailu multzoak";
// MARK: Reaction history
"reaction_history_title" = "Erreakzioak";

View file

@ -2,5 +2,5 @@
"NSCameraUsageDescription" = "L'appareil photo est utilisé pour prendre des photos et des vidéos et pour passer des appels vidéo.";
"NSPhotoLibraryUsageDescription" = "La photothèque est utilisée pour envoyer des photos et des vidéos.";
"NSMicrophoneUsageDescription" = "Le microphone est utilisé pour prendre des vidéos et passer des appels.";
"NSContactsUsageDescription" = "Afin dafficher qui parmis vos contacts utilise déjà Riot ou Matrix, nous pouvons envoyer les adresses e-mails et les numéros de téléphone de votre carnet d'adresse à votre Serveur d'Identité Matrix. New Vector ne stocke pas ces données et ne les utilise pas à d'autres fins. Pour plus d'informations, veuillez consulter la page de politique de confidentialité dans les paramètres de l'application.";
"NSContactsUsageDescription" = "Pour découvrir vos contacts qui utilisent déjà Matrix, Riot peut envoyer les adresses e-mails et les numéros de téléphone de votre carnet dadresse à votre serveur didentité Matrix. Si votre serveur didentité le prend en charge, les données personnelles sont hachées avant lenvoi  vérifiez sa politique de vie privée pour plus de détails.";
"NSCalendarsUsageDescription" = "Voir vos rendez-vous prévus dans lapplication.";

View file

@ -303,9 +303,9 @@
"settings_confirm_password" = "confirmer le mot de passe";
"settings_fail_to_update_password" = "Échec de la modification du mot de passe";
"settings_password_updated" = "Votre mot de passe a été modifié";
"settings_crypto_device_name" = "Nom de l'appareil : ";
"settings_crypto_device_name" = "Nom public de lappareil : ";
"settings_crypto_device_id" = "\nIdentifiant de l'appareil : ";
"settings_crypto_device_key" = "\nClé de l'appareil : ";
"settings_crypto_device_key" = "\nClé de lappareil :\n";
"settings_crypto_export" = "Exporter les clés";
"settings_crypto_blacklist_unverified_devices" = "Chiffrer uniquement vers les appareils vérifiés";
// Room Details
@ -456,7 +456,7 @@
// Events formatter
"event_formatter_member_updates" = "%tu modifications dans les membres";
"auth_home_server_placeholder" = "URL (par ex. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (par ex. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (par ex. https://vector.im)";
"room_ongoing_conference_call_with_close" = "Téléconférence en cours. Rejoindre en %@ ou %@. %@.";
"room_ongoing_conference_call_close" = "Fermer";
"room_conference_call_no_power" = "Permissions requises pour gérer la téléconférence dans ce salon";
@ -821,3 +821,93 @@
"image_picker_action_library" = "Choisir dans la médiathèque";
"camera_unavailable" = "Lappareil photo nest pas disponible sur votre appareil";
"photo_library_access_not_granted" = "%@ na pas la permission pour accéder à la médiathèque, veuillez modifier les options de vie privée";
"auth_forgot_password_error_no_configured_identity_server" = "Aucun serveur didentité nest configuré : ajoutez-en un pour réinitialiser votre mot de passe.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Aucun serveur didentité nest configuré donc vous ne pouvez pas ajouter de participant avec un e-mail.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Aucun serveur didentité nest configuré donc vous ne pouvez pas commencer de discussion avec un contact en utilisant un e-mail.";
// Service terms
"service_terms_modal_title" = "Conditions de service";
"service_terms_modal_message" = "Pour continuer vous devez accepter les conditions de ce service (%@).";
"service_terms_modal_accept_button" = "Accepter";
"service_terms_modal_description_for_identity_server" = "Se rendre découvrable pour les autres";
"service_terms_modal_description_for_integration_manager" = "Utiliser des robots, des passerelles, des widgets et des packs de stickers";
"room_participants_remove_third_party_invite_prompt_msg" = "Voulez-vous vraiment retirer cette invitation ?";
// Errors
"error_user_already_logged_in" = "On dirait que vous essayez de vous connecter à un autre serveur daccueil. Voulez-vous vous déconnecter ?";
"room_accessiblity_scroll_to_bottom" = "Défiler vers le bas";
"room_accessibility_search" = "Chercher";
"room_accessibility_integrations" = "Intégrations";
"room_accessibility_upload" = "Envoyer";
"room_accessibility_call" = "Appeler";
"room_accessibility_hangup" = "Raccrocher";
"media_type_accessibility_image" = "Image";
"media_type_accessibility_audio" = "Audio";
"media_type_accessibility_video" = "Vidéo";
"media_type_accessibility_location" = "Emplacement";
"media_type_accessibility_file" = "Fichier";
"media_type_accessibility_sticker" = "Sticker";
// Widget Picker
"widget_picker_title" = "Intégrations";
"auth_add_email_message_2" = "Définissez une adresse e-mail pour la récupération de compte, et pour être éventuellement découvrable par les personnes qui vous connaissent.";
"auth_add_phone_message_2" = "Définissez un numéro de téléphone pour être éventuellement découvrable par les personnes qui vous connaissent.";
"auth_add_email_phone_message_2" = "Définissez une adresse e-mail pour la récupération de compte. Vous pouvez ensuite utiliser votre e-mail ou votre numéro de téléphone pour être découvrable par les personnes qui vous connaissent.";
"auth_email_is_required" = "Aucun serveur didentité nest configuré donc vous ne pouvez pas ajouter dadresse e-mail pour pouvoir réinitialiser votre mot de passe dans le futur.";
"auth_phone_is_required" = "Aucun serveur didentité nest configuré donc vous ne pouvez pas ajouter de numéro de téléphone pour pouvoir réinitialiser votre mot de passe dans le futur.";
"auth_reset_password_error_is_required" = "Aucun serveur didentité nest configuré : ajoutez-en un dans les options du serveur pour réinitialiser votre mot de passe.";
"contacts_address_book_no_identity_server" = "Aucun serveur didentité nest configuré";
"settings_discovery_settings" = "DÉCOUVERTE";
"settings_identity_server_settings" = "SERVEUR DIDENTITÉ";
"settings_three_pids_management_information_part1" = "Gérez quelles adresse e-mail et quels numéros de téléphone vous pouvez utiliser pour vous connecter ou récupérer votre compte ici. Contrôlez qui vous pouvez trouver dans ";
"settings_three_pids_management_information_part2" = "Découverte";
"settings_three_pids_management_information_part3" = ".";
"settings_calls_stun_server_fallback_button" = "Autoriser le serveur dassistance dappel de secours";
"settings_calls_stun_server_fallback_description" = "Autorise le serveur dassistance dappel de secours %@ quand votre serveur daccueil nen offre pas (votre adresse IP serait partagée lors dun appel).";
"settings_devices_description" = "Le nom public de lappareil est visible pour les personnes avec qui vous communiquez";
"settings_discovery_no_identity_server" = "Vous nutilisez actuellement aucun serveur didentité. Pour être découvrable par les contacts existants que vous connaissez, ajoutez-en un.";
"settings_discovery_terms_not_signed" = "Acceptez les conditions de service du serveur didentité (%@) pour vous permettre dêtre découvrable avec une adresse e-mail ou un numéro de téléphone.";
"settings_discovery_three_pids_management_information_part1" = "Gérez quelles adresses e-mail ou numéros de téléphone peuvent être utilisés par les autres utilisateurs pour vous découvrir et pour vous inviter dans des salons. Ajoutez ou supprimez des adresses e-mail ou des numéros de téléphones depuis cette liste dans les ";
"settings_discovery_three_pids_management_information_part2" = "Paramètres utilisateur";
"settings_discovery_three_pids_management_information_part3" = ".";
"settings_discovery_error_message" = "Une erreur est survenue. Réessayez.";
"settings_discovery_three_pid_details_title_email" = "Gérer le-mail";
"settings_discovery_three_pid_details_information_email" = "Gérer les préférences pour cette adresse e-mail, que les autres utilisateurs peuvent utiliser pour vous découvrir et pour vous inviter dans des salons. Ajoutez ou supprimez des adresses e-mail dans Comptes.";
"settings_discovery_three_pid_details_title_phone_number" = "Gérer le numéro de téléphone";
"settings_discovery_three_pid_details_information_phone_number" = "Gérer les préférences pour ce numéro de téléphone, que les autres utilisateurs peuvent utiliser pour vous découvrir et pour vous inviter dans des salons. Ajoutez ou supprimez des numéros de téléphone dans Comptes.";
"settings_discovery_three_pid_details_share_action" = "Partager";
"settings_discovery_three_pid_details_revoke_action" = "Révoquer";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "Annuler la validation de le-mail";
"settings_discovery_three_pid_details_enter_sms_code_action" = "Saisir le code dactivation par SMS";
"settings_identity_server_description" = "En utilisant le serveur didentité ci-dessus, vous pouvez découvrir et être découvrable par les contacts existants que vous connaissez.";
"settings_identity_server_no_is" = "Aucun serveur didentité nest configuré";
"settings_identity_server_no_is_description" = "Vous nutilisez actuellement aucun serveur didentité. Pour découvrir et être découvrable par les contacts existants que vous connaissez, ajoutez-en un ci-dessus.";
// Identity server settings
"identity_server_settings_title" = "Serveur didentité";
"identity_server_settings_description" = "Vous utilisez actuellement %@ pour découvrir et être découvrable par les contacts existants que vous connaissez.";
"identity_server_settings_no_is_description" = "Vous nutilisez actuellement aucun serveur didentité. Pour découvrir et être découvrable par vos contacts existants, ajoutez-en un ci-dessus.";
"identity_server_settings_place_holder" = "Saisir un serveur didentité";
"identity_server_settings_add" = "Ajouter";
"identity_server_settings_change" = "Modifier";
"identity_server_settings_disconnect_info" = "La déconnexion de votre serveur didentité signifie que vous ne pourrez pas être découvrable par les autres utilisateurs et inviter dautres personnes par e-mail ou par téléphone.";
"identity_server_settings_disconnect" = "Se déconnecter";
"identity_server_settings_alert_no_terms_title" = "Le serveur didentité na pas de conditions de service";
"identity_server_settings_alert_no_terms" = "Le serveur didentité que vous avez choisi na pas de conditions de service. Continuez uniquement si vous faites confiance au propriétaire de ce serveur.";
"identity_server_settings_alert_change_title" = "Modifier le serveur didentité";
"identity_server_settings_alert_change" = "Se déconnecter du serveur didentité %1$@ et se connecter à %2$@ à la place ?";
"identity_server_settings_alert_disconnect_title" = "Se déconnecter du serveur didentité";
"identity_server_settings_alert_disconnect" = "Se déconnecter du serveur didentité %@ ?";
"identity_server_settings_alert_disconnect_button" = "Se déconnecter";
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "Vous partagez toujours vos données personnelles sur le serveur didentité %@.\n\nNous recommandons que vous supprimiez vos adresses e-mail et vos numéros de téléphone du serveur didentité avant de vous déconnecter.";
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Se déconnecter quand même";
"identity_server_settings_alert_error_terms_not_accepted" = "Vous devez accepter les conditions de %@ pour le définir comme serveur didentité.";
"identity_server_settings_alert_error_invalid_identity_server" = "%@ nest pas un serveur didentité valide.";
"call_no_stun_server_error_title" = "Lappel a échoué en raison dun serveur mal configuré";
"call_no_stun_server_error_message_1" = "Demandez à ladministrateur de votre serveur daccueil %@ de configurer un serveur TURN afin que les appels fonctionnent de manière fiable.";
"call_no_stun_server_error_message_2" = "Sinon, vous pouvez essayer dutiliser le serveur public à %@, mais ça ne sera pas fiable et ça partagera votre adresse IP avec ce serveur. Vous pouvez aussi gérer cela dans les Paramètres";
"call_no_stun_server_error_use_fallback_button" = "Essayer dutiliser %@";
// Generic errors
"error_invite_3pid_with_no_identity_server" = "Ajoutez un serveur didentité dans vos paramètres pour inviter par e-mail.";
"service_terms_modal_decline_button" = "Refuser";
"service_terms_modal_description_for_identity_server_1" = "Trouver dautres personnes par téléphone ou par e-mail";
"service_terms_modal_description_for_identity_server_2" = "Être trouvé par téléphone ou par e-mail";
// Service terms - Variant for identity server when displayed out of a context
"service_terms_modal_title_identity_server" = "Découverte des contacts";
"service_terms_modal_message_identity_server" = "Acceptez les conditions du serveur didentité (%@) pour découvrir des contacts.";

View file

@ -2,5 +2,5 @@
"NSCameraUsageDescription" = "A kamera fényképek, videók készítéséhez és videóhívásokhoz lesz használva.";
"NSPhotoLibraryUsageDescription" = "A fénykép galéria fényképek és videók küldéséhez lesz használva.";
"NSMicrophoneUsageDescription" = "A mikrofon videók készítéséhez és hívásokhoz lesz használva.";
"NSContactsUsageDescription" = "Ahhoz, hogy meg tudjuk mutatni melyik ismerősöd használja már a Riot-ot vagy Matrix-ot, el tudjuk küldeni az e-mail címeket és telefonszámokat a címjegyzékedből a Matrix Azonosítási Szerverének. „New Vector” nem tárolja és semmilyen más célra nem használja ezeket az információkat. További információkért olvasd el az adatkezelési oldalt az alkalmazás beállításaiban.";
"NSContactsUsageDescription" = "Az olyan ismerősök felderítéséhez akik már használják a Matrixot, Riot el tudja küldeni a címjegyzékben található e-mail címeket és telefonszámokat az általad választott Matrix azonosítási szervernek. Ahol lehetséges a személyes adatok hash-elve lesznek - kérlek ellenőrizd az azonosítási szervered adatvédelmi szabályait.";
"NSCalendarsUsageDescription" = "Nézd meg a találkozóidat az alkalmazásban.";

View file

@ -54,7 +54,7 @@
"auth_repeat_password_placeholder" = "Jelszó megerősítés";
"auth_repeat_new_password_placeholder" = "Új jelszó megerősítés";
"auth_home_server_placeholder" = "URL (pl. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (pl. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (pl. https://vector.im)";
"auth_invalid_login_param" = "Hibás felhasználói név és/vagy jelszó";
"auth_invalid_user_name" = "A felhasználói név tartalmazhat betűket, számokat, pontokat, kötőjeleket és aláhúzást";
"auth_invalid_password" = "Jelszó túl rövid (min. 6 karakter)";
@ -349,9 +349,9 @@
"settings_confirm_password" = "jelszó megerősítése";
"settings_fail_to_update_password" = "A jelszó frissítése nem sikerült";
"settings_password_updated" = "A jelszavad frissítve";
"settings_crypto_device_name" = "Eszköz neve: ";
"settings_crypto_device_name" = "Eszköz nyilvános neve: ";
"settings_crypto_device_id" = "\nEszköz azonosítója: ";
"settings_crypto_device_key" = "\nEszköz kulcsa: ";
"settings_crypto_device_key" = "\nEszköz kulcsa:\n";
"settings_crypto_export" = "Kulcsok kimentése";
"settings_crypto_blacklist_unverified_devices" = "Csak ellenőrzött eszközöknek titkosít";
"settings_deactivate_my_account" = "Felhasználói fiókom felfüggesztése";
@ -818,3 +818,95 @@
"room_event_action_reaction_history" = "Reakciók története";
// MARK: Reaction history
"reaction_history_title" = "Reakciók";
"auth_forgot_password_error_no_configured_identity_server" = "Azonosítási szerver nincs beállítva: a jelszó visszaállításhoz adj hozzá egyet.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Azonosítási szerver nincs beállítva, így e-mail cím alapján nem tudsz hozzáadni résztvevőt.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Azonosítási szerver nincs beállítva, így e-mail cím alapján nem tudsz beszélgetést kezdeményezni.";
"room_action_camera" = "Fotó vagy videó készítése";
// Media picker
"media_picker_title" = "Média könyvtár";
// Image picker
"image_picker_action_camera" = "Fotó készítése";
"image_picker_action_library" = "Válassz a könyvtárból";
"camera_unavailable" = "A kamera nem érhető el az eszközödön";
"photo_library_access_not_granted" = "A fénykép könyvár eléréséhez %@ nem rendelkezik engedéllyel, kérlek változtasd meg az adatvédelmi beállításokat";
// Service terms
"service_terms_modal_title" = "Felhasználási feltételek";
"service_terms_modal_message" = "A folytatáshoz el kell fogadnod a felhasználási feltételeket (%@).";
"service_terms_modal_accept_button" = "Elfogad";
"service_terms_modal_description_for_identity_server" = "Látható mások számára";
"service_terms_modal_description_for_integration_manager" = "Használjon botokat, hidakat, kisalkalmazásokat és matrica csomagokat";
"room_participants_remove_third_party_invite_prompt_msg" = "Biztos vagy benne, hogy visszavonod ezt a meghívót?";
// Errors
"error_user_already_logged_in" = "Úgy tűnik másik matrix szerverhez próbálsz csatlakozni. Ki szeretnél jelentkezni?";
"room_accessiblity_scroll_to_bottom" = "Görgess az aljára";
"room_accessibility_search" = "Keresés";
"room_accessibility_integrations" = "Integrációk";
"room_accessibility_upload" = "Feltöltés";
"room_accessibility_call" = "Hívás";
"room_accessibility_hangup" = "Letesz";
"media_type_accessibility_image" = "Kép";
"media_type_accessibility_audio" = "Hang";
"media_type_accessibility_video" = "Videó";
"media_type_accessibility_location" = "Helyzet";
"media_type_accessibility_file" = "Fájl";
"media_type_accessibility_sticker" = "Matrica";
// Widget Picker
"widget_picker_title" = "Integrációk";
"auth_add_email_message_2" = "E-mail cím beállítása fiók visszaállításhoz és, hogy később az ismerősök megtalálhassanak.";
"auth_add_phone_message_2" = "Telefonszám beállítása, hogy később az ismerősök megtalálhassanak.";
"auth_add_email_phone_message_2" = "E-mail cím beállítása fiók visszaállításhoz. Használj később e-mail címet vagy telefonszámot, hogy az ismerősök megtalálhassanak.";
"auth_email_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni e-mail címet amivel vissza lehetne állítani a jelszót a későbbiekben.";
"auth_phone_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni telefonszámot, hogy vissza lehetne állítani a jelszót a későbbiekben.";
"auth_reset_password_error_is_required" = "Azonosítási szerver nincs beállítva: adj hozzá egyet a szerver beállításoknál, hogy a jelszót vissza lehessen állítani.";
"contacts_address_book_no_identity_server" = "Azonosítási szerver nincs beállítva";
"settings_discovery_settings" = "FELDERÍTÉS";
"settings_identity_server_settings" = "AZONOSÍTÁSI SZERVER";
"settings_three_pids_management_information_part1" = "Állítsd be melyik e-mail címmel vagy telefonszámmal lehessen bejelentkezni vagy visszaállítani a fiókot. Add meg ki találhat meg itt: ";
"settings_three_pids_management_information_part2" = "Felderítés";
"settings_three_pids_management_information_part3" = ".";
"settings_calls_stun_server_fallback_button" = "Másodlagos hívást segítő szerver engedélyezése";
"settings_calls_stun_server_fallback_description" = "Másodlagos hívást segítő szerver (%@) engedélyezése ha a matrix szervered nem ajánl fel másikat (az IP címed a hívás ideje alatt meg lesz osztva).";
"settings_devices_description" = "Az eszköz nyilvános neve látható azoknál az embereknél akikkel beszélgetsz";
"settings_discovery_no_identity_server" = "Jelenleg nem használsz azonosítási szervert. Ahhoz, hogy az ismerősök megtalálhassanak adj hozzá egyet.";
"settings_discovery_terms_not_signed" = "Egyetértés az Azonosítási Szerver (%@) Felhasználási Feltételeivel, hogy e-mail címmel vagy telefonszámmal megtalálható lehess.";
"settings_discovery_three_pids_management_information_part1" = "E-mail címek és telefonszámok beállítása amivel más felhasználók megtalálhatnak és meghívhatna szobákba. E-mail cím és telefonszám hozzáadása és törlése a listából itt: ";
"settings_discovery_three_pids_management_information_part2" = "Felhasználói Beállítások";
"settings_discovery_three_pids_management_information_part3" = ".";
"settings_discovery_error_message" = "Hiba történt. Kérlek próbáld újra.";
"settings_discovery_three_pid_details_title_email" = "E-mail címek kezelése";
"settings_discovery_three_pid_details_information_email" = "E-mail cím beállítások kezelése, amivel más felhasználók megtalálhatnak és meghívhatnak a szobákba. E-mail cím hozzáadása és törlése a Fiókokban.";
"settings_discovery_three_pid_details_title_phone_number" = "Telefonszám kezelése";
"settings_discovery_three_pid_details_information_phone_number" = "Telefonszám beállítások kezelése, amivel más felhasználók megtalálhatnak és meghívhatnak a szobákba. Telefonszám hozzáadása és törlése a Fiókokban.";
"settings_discovery_three_pid_details_share_action" = "Megoszt";
"settings_discovery_three_pid_details_revoke_action" = "Visszavon";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "E-mail azonosítás visszavonása";
"settings_discovery_three_pid_details_enter_sms_code_action" = "Add meg az SMS aktiválási kódot";
"settings_identity_server_description" = "Az alábbi azonosítási szerver használatával megtalálhatod a barátaidat és te is megtalálható leszel az ismerőseidnek.";
"settings_identity_server_no_is" = "Azonosítási szerver nincs beállítva";
"settings_identity_server_no_is_description" = "Jelenleg nem használsz azonosítási szervert. Ahhoz, hogy megtalálhass másokat és az ismerősök megtalálhassanak adj hozzá egyet alább.";
// Identity server settings
"identity_server_settings_title" = "Azonosítási Szerver";
"identity_server_settings_description" = "Jelenleg ezt használod: %@, hogy megtalálj másokat és megtalálhassanak ismerősök.";
"identity_server_settings_no_is_description" = "Jelenleg nem használsz azonosítási szervert. Ahhoz, hogy megtalálj másokat és megtalálhassanak ismerősök adj meg egyet alább.";
"identity_server_settings_place_holder" = "Adj meg egy azonosítási szervet";
"identity_server_settings_add" = "Hozzáad";
"identity_server_settings_change" = "Módosít";
"identity_server_settings_disconnect_info" = "Ha lecsatlakozol az azonosítási szerverről azt eredményezi, hogy mások nem fognak megtalálni és nem tudnak meghívni e-mail vagy telefonszám alapján.";
"identity_server_settings_disconnect" = "Lecsatlakozás";
"identity_server_settings_alert_no_terms_title" = "Az azonosítási szervernek nincs felhasználási feltétele";
"identity_server_settings_alert_no_terms" = "Az általad választott azonosítási szervernek nincs felhasználási feltétele. Csak akkor lépj tovább ha megbízol a szolgáltatás tulajdonosában.";
"identity_server_settings_alert_change_title" = "Azonosítási szerver megváltoztatása";
"identity_server_settings_alert_change" = "Lecsatlakozol erről az azonosítási szerverről: %1$@ és csatlakozol ehhez: %2$@?";
"identity_server_settings_alert_disconnect_title" = "Azonosítási szerverről lecsatlakozás";
"identity_server_settings_alert_disconnect" = "Lecsatlakozol erről az azonosítási szerverről: %@?";
"identity_server_settings_alert_disconnect_button" = "Lecsatlakozás";
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "Továbbra is megosztod a személyes adataidat ezzel az azonosítási szerverrel: %@.\n\nAzt javasoljuk, hogy az azonosítási szerverről való lecsatlakozás előtt először töröld az e-mail címeidet és telefonszámaidat.";
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Mindenképpen lecsatlakozás";
"identity_server_settings_alert_error_terms_not_accepted" = "Mielőtt beállíthatnád azonosítási szervernek el kell fogadnod a feltételeket: %@.";
"identity_server_settings_alert_error_invalid_identity_server" = "%@ nem egy érvényes azonosítási szerver.";
"call_no_stun_server_error_title" = "A hívás a hibásan konfigurált szerver miatt sikertelen";
"call_no_stun_server_error_message_1" = "Kérlek kérd meg a matrix szervered (%@) adminisztrátorát, hogy állítson be egy TURN szervert a hívások megbízható működéséhez.";
"call_no_stun_server_error_message_2" = "Alternatív megoldásként használhatod a nyilvános szervert itt: %@, de ez lehet, hogy nem lesz annyira megbízható és megosztja az IP címedet a szerverrel. Ezt a Beállításokban módosíthatod";
"call_no_stun_server_error_use_fallback_button" = "Próbáld használni ezt: %@";
// Generic errors
"error_invite_3pid_with_no_identity_server" = "Az e-mail címmel való meghíváshoz adj hozzá egy azonosítási szervert a beállításaidnál.";

View file

@ -2,4 +2,5 @@
"NSCameraUsageDescription" = "La fotocamera viene utilizzata per scattare fotografie, registrare video ed eseguire videochiamate.";
"NSPhotoLibraryUsageDescription" = "La libreria fotografica viene utilizzata per inviare foto e video.";
"NSMicrophoneUsageDescription" = "Il microfono viene utilizzato per registrare video ed effettuare chiamate.";
"NSContactsUsageDescription" = "Per mostrare i tuoi contatti che stanno già usando Riot o Matrix, possiamo inviare gli indirizzi email e i numeri di telefono della tua rubrica al tuo server identità. New Vector non memorizza e non usa per altri scopi questi dati. Per maggiori informazioni guarda l'informativa sulla privacy presente nelle impostazioni di questa applicazione.";
"NSContactsUsageDescription" = "Per scoprire i contatti che già usano Matrix, Riot può inviare gli indirizzi email e i numeri di telefono della tua rubrica al server identità che hai scelto. Se supportato, viene fatto un hash dei dati personali prima dell'invio - controlla la politica sulla privacy del tuo server di identità per maggiori informazioni.";
"NSCalendarsUsageDescription" = "Vedi le tue riunioni programmate nell'app.";

View file

@ -36,7 +36,7 @@
"auth_repeat_password_placeholder" = "Ripeti password";
"auth_repeat_new_password_placeholder" = "Conferma la nuova password";
"auth_home_server_placeholder" = "URL (es. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (es. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (es. https://vector.im)";
"auth_invalid_login_param" = "Nome utente e/o password non corretti";
"auth_invalid_user_name" = "Il nome utente può contenere solo lettere, numeri, punti, trattini e linee basse";
"auth_invalid_password" = "Password troppo breve (min 6)";
@ -174,7 +174,7 @@
"room_participants_filter_room_members" = "Filtra i membri della stanza";
"room_participants_invite_another_user" = "Cerca / invita per ID utente, nome o email";
"room_participants_invite_malformed_id_title" = "Errore durante l'invito";
"room_participants_invite_malformed_id" = "ID malformato. Dovrebbe essere un indirizzo email o un ID Matrix come '@localpart:domain'";
"room_participants_invite_malformed_id" = "ID incorretto. Dovrebbe essere un indirizzo email o un ID Matrix come '@localpart:domain'";
"room_participants_invited_section" = "INVITATI";
"room_participants_online" = "Online";
"room_participants_offline" = "Offline";
@ -201,7 +201,7 @@
"room_participants_action_start_video_call" = "Avvia videochiamata";
"room_participants_action_mention" = "Citazione";
// Chat
"room_jump_to_first_unread" = "Primo messaggio non letto";
"room_jump_to_first_unread" = "Vai al primo messaggio non letto";
"room_new_message_notification" = "%d nuovo messaggio";
"room_new_messages_notification" = "%d nuovi messaggi";
"room_one_user_is_typing" = "%@ sta scrivendo…";
@ -221,10 +221,10 @@
"room_ongoing_conference_call" = "Chiamata di conferenza in corso. Unisciti come %@ o %@.";
"room_ongoing_conference_call_with_close" = "Chiamata di conferenza in corso. Unisciti come %@ o %@. %@.";
"room_ongoing_conference_call_close" = "Chiudi";
"room_conference_call_no_power" = "Hai bisogno i permessi per gestire le chiamate di gruppo in questa stanza";
"room_conference_call_no_power" = "Hai bisogno dei permessi per gestire le chiamate di gruppo in questa stanza";
"room_prompt_resend" = "Rinvia tutto";
"room_prompt_cancel" = "annulla tutto";
"room_resend_unsent_messages" = "Rinvia i messaggi non spediti";
"room_resend_unsent_messages" = "Reinvia i messaggi non spediti";
"room_delete_unsent_messages" = "Elimina messaggi non spediti";
"room_event_action_copy" = "Copia";
"room_event_action_quote" = "Cita";
@ -233,19 +233,19 @@
"room_event_action_share" = "Condividi";
"room_event_action_permalink" = "Collegamento permanente";
"room_event_action_view_source" = "Vedi sorgente";
"room_event_action_view_decrypted_source" = "Vedi sorgente decriptato";
"room_event_action_view_decrypted_source" = "Vedi sorgente decriptata";
"room_event_action_report" = "Segnala contenuto";
"room_event_action_report_prompt_reason" = "Motivo per segnalare questo contenuto";
"room_event_action_kick_prompt_reason" = "Motivo per buttare fuori questo utente";
"room_event_action_ban_prompt_reason" = "Motivo per bandire questo utente";
"room_event_action_report_prompt_reason" = "Motivo della segnalazione";
"room_event_action_kick_prompt_reason" = "Motivo per cui rimuovi questo utente";
"room_event_action_ban_prompt_reason" = "Motivo per cui banni questo utente";
"room_event_action_report_prompt_ignore_user" = "Vuoi nascondere tutti i messaggi da questo utente?";
"room_event_action_save" = "Salva";
"room_event_action_resend" = "Rinvia";
"room_event_action_resend" = "Reinvia";
"room_event_action_delete" = "Elimina";
"room_event_action_cancel_send" = "Annulla invio";
"room_event_action_cancel_download" = "Annulla download";
"room_event_action_view_encryption" = "Informazioni crittografia";
"room_warning_about_encryption" = "La crittografia da-utente-a-utente è in fase sperimentale e potrebbe non essere affidabile.\n\nNon dovesti ancora fidarti di questo per proteggere i tuoi dati.\n\nI dispositivi non potranno decifrare la cronologia dei messaggi precedenti all'entrata nella stanza.\n\nI messaggi cifrati non saranno visibili dalle applicazioni che non hanno ancora implementato la crittografia.";
"room_warning_about_encryption" = "La crittografia da-utente-a-utente è in fase sperimentale e potrebbe non esser ancora affidabile.\n\nNon dovesti ancora farci affidamento per proteggere i tuoi dati.\n\nI dispositivi non potranno decrittare la cronologia dei messaggi precedenti all'entrata nella stanza.\n\nI messaggi crittografati non saranno visibili dalle applicazioni che non hanno ancora implementato la crittografia.";
"room_event_failed_to_send" = "Invio fallito";
"room_action_send_photo_or_video" = "Invia foto o video";
"room_action_send_sticker" = "Invia sticker";
@ -262,7 +262,7 @@
"room_resource_usage_limit_reached_message_contact_3" = " per aumentare questo limite.";
// Unknown devices
"unknown_devices_alert_title" = "La stanza contiene dispositivi sconosciuti";
"unknown_devices_alert" = "Questa stanza contiene dispositivi sconosciuti che non sono stati verificati.\nCiò significa che non esiste alcuna garanzia che i dispositivi appartengano agli utenti che ne rivendicano il possesso.\nSi consiglia di passare attraverso il processo di verifica per ogni dispositivo prima di continuare, ma è possibile inviare ugualmente il messaggio senza fare la verifica se preferisci.";
"unknown_devices_alert" = "Questa stanza contiene dispositivi sconosciuti che non sono stati verificati.\nCiò significa che non esiste alcuna garanzia che i dispositivi siano davvero quelli di chi dice di possederli.\nPrima di continuare si consiglia di effettuare la verifica di ogni dispositivo, ma se vuoi è comunque possibile rispedire il messaggio senza fare alcuna verifica.";
"unknown_devices_send_anyway" = "Invia comunque";
"unknown_devices_call_anyway" = "Chiama comunque";
"unknown_devices_answer_anyway" = "Rispondi comunque";
@ -277,19 +277,19 @@
"room_title_one_member" = "1 membro";
// Room Preview
"room_preview_invitation_format" = "Sei stato invitato ad unirti in questa stanza da %@";
"room_preview_subtitle" = "Questa è l'anteprima della stanza. Le interazioni con la stanza sono state disabilitate.";
"room_preview_unlinked_email_warning" = "Questo invito è stato spedito da %@, che non è associato a questo account. È possibile che tu voglia connetterti con un altro account, o aggiungere l'email al tuo account.";
"room_preview_subtitle" = "Questa è l'anteprima della stanza. Le interazioni con la stanza sono disabilitate.";
"room_preview_unlinked_email_warning" = "Questo invito è stato spedito da %@, che non è associato a questo account. È possibile che tu voglia connetterti con un altro account o aggiungere questo indirizzo email al tuo account.";
"room_preview_try_join_an_unknown_room" = "Stai provando ad accedere a %@. Desideri entrare per partecipare alla discussione?";
"room_preview_try_join_an_unknown_room_default" = "una stanza";
// Settings
"settings_title" = "Impostazioni";
"account_logout_all" = "Sconnetti tutti gli account";
"settings_config_no_build_info" = "Nessuna informazione di compilazione";
"settings_config_no_build_info" = "Nessuna informazione di build";
"settings_mark_all_as_read" = "Segna tutti i messaggi come letti";
"settings_report_bug" = "Segnala errore";
"settings_clear_cache" = "Elimina cache";
"settings_config_home_server" = "Il server home è %@";
"settings_config_identity_server" = "Il server identità è %@";
"settings_config_home_server" = "L'Homeserver è %@";
"settings_config_identity_server" = "L'Identity server è %@";
"settings_config_user_id" = "Connesso come %@";
"settings_user_settings" = "IMPOSTAZIONI UTENTE";
"settings_notifications_settings" = "IMPOSTAZIONI NOTIFICHE";
@ -303,11 +303,11 @@
"settings_flair" = "Mostra predisposizione se permesso";
"settings_devices" = "DISPOSITIVI";
"settings_cryptography" = "CRITTOGRAFIA";
"settings_key_backup" = "BACKUP CHIAVE";
"settings_key_backup" = "BACKUP DELLE CHIAVI";
"settings_deactivate_account" = "DISATTIVA ACCOUNT";
"settings_sign_out" = "Disconnetti";
"settings_sign_out_confirmation" = "Sei sicuro?";
"settings_sign_out_e2e_warn" = "Perderai le tue chiavi di crittografia da-utente-a-utente. Questo significa che non potrai più leggere i vecchi messaggi nelle stanze cifrate su questo dispositivo.";
"settings_sign_out_e2e_warn" = "Perderai le tue chiavi di crittografia da-utente-a-utente. Questo significa che non potrai più leggere i vecchi messaggi nelle stanze crittografate su questo dispositivo.";
"settings_profile_picture" = "Immagine profilo";
"settings_display_name" = "Nome visualizzato";
"settings_first_name" = "Nome";
@ -324,8 +324,8 @@
"settings_night_mode" = "Modalità notte";
"settings_fail_to_update_profile" = "Errore nell'aggiornamento del profilo";
"settings_enable_push_notif" = "Notifiche per questo dispositivo";
"settings_show_decrypted_content" = "Mostra contenuto decriptato";
"settings_global_settings_info" = "Impostazioni globali di notifica sono disponibili nel tuo %@ web client";
"settings_show_decrypted_content" = "Mostra contenuto decrittato";
"settings_global_settings_info" = "Le impostazioni globali di notifica sono disponibili nel tuo %@ web client";
"settings_pin_rooms_with_missed_notif" = "Segna le stanze con notifiche perse";
"settings_pin_rooms_with_unread" = "Segna le stanze con messaggi non letti";
"settings_on_denied_notification" = "Le notifiche non sono permesse per %@, abilitale nelle impostazioni del tuo dispositivo";
@ -345,15 +345,15 @@
"settings_labs_e2e_encryption" = "Crittografia da-utente-a-utente";
"settings_labs_e2e_encryption_prompt_message" = "Per finire la configurazione della crittografia devi rieseguire l'accesso.";
"settings_labs_room_members_lazy_loading" = "Caricamento posticipato dei membri della stanza";
"settings_labs_room_members_lazy_loading_error_message" = "Il tuo server home non supporta ancora il caricamento posticipato dei membri delle stanze. Prova in seguito.";
"settings_labs_room_members_lazy_loading_error_message" = "Il tuo Homeserver non supporta ancora il caricamento intelligente dei membri delle stanze. Prova in seguito.";
"settings_labs_create_conference_with_jitsi" = "Crea una videoconferenza con jitsi";
"settings_version" = "Versione %@";
"settings_olm_version" = "Versione olm %@";
"settings_olm_version" = "Versione Olm %@";
"settings_copyright" = "Copyright";
"settings_copyright_url" = "https://riot.im/copyright";
"settings_term_conditions" = "Termini e condizioni";
"settings_term_conditions_url" = "https://riot.im/tac_apple";
"settings_privacy_policy" = "Politica sulla privacy";
"settings_privacy_policy" = "Privacy Policy";
"settings_privacy_policy_url" = "https://riot.im/privacy";
"settings_third_party_notices" = "Avvisi di terze parti";
"settings_send_crash_report" = "Invia dati di utilizzo anonimi";
@ -361,22 +361,22 @@
"settings_old_password" = "vecchia password";
"settings_new_password" = "nuova password";
"settings_confirm_password" = "conferma password";
"settings_fail_to_update_password" = "Fallito l'aggiornamento password";
"settings_fail_to_update_password" = "Aggiornamento password fallito";
"settings_password_updated" = "La tua password è stata aggiornata";
"settings_crypto_device_name" = "Nome dispositivo: ";
"settings_crypto_device_name" = "Nome pubblico dispositivo: ";
"settings_crypto_device_id" = "\nID Dispositivo: ";
"settings_crypto_device_key" = "\nChiave dispositivo: ";
"settings_crypto_device_key" = "\nChiave dispositivo:\n";
"settings_crypto_export" = "Esporta chiavi";
"settings_crypto_blacklist_unverified_devices" = "Cripta solo per i dispositivi verificati";
"settings_crypto_blacklist_unverified_devices" = "Crittografa solo per i dispositivi verificati";
"settings_deactivate_my_account" = "Disattiva il mio account";
"settings_key_backup_info" = "I messaggi criptati sono protetti con la cifratura end-to-end. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi.";
"settings_key_backup_info" = "I messaggi crittografati sono protetti con la crittografia da-utente-a-utente. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi.";
"settings_key_backup_info_checking" = "Verifica...";
"settings_key_backup_info_none" = "Nessun backup programmato per le chiavi da questo dispositivo.";
"settings_key_backup_info_signout_warning" = "Fai un backup sicuro delle tue chiavi prima di uscire per evitare di perderle.";
"settings_key_backup_info_none" = "Nessun Backup programmato per le Chiavi da questo dispositivo.";
"settings_key_backup_info_signout_warning" = "Connetti questo dispositivo al backup chiavi prima di disconnetterti per evitare di perdere eventuali chiavi presenti solo qui.";
"settings_key_backup_info_version" = "Versione backup chiave: %@";
"settings_key_backup_info_algorithm" = "Algoritmo: %@";
"settings_key_backup_info_valid" = "Questo dispositivo sta eseguendo il backup delle tue chiavi.";
"settings_key_backup_info_not_valid" = "Questo dispositivo non sta eseguendo il backup delle tue chiavi.";
"settings_key_backup_info_not_valid" = "Questo dispositivo non sta eseguendo il Backup delle tue Chiavi, ma é disponibile un vecchio Backup da cui puoi ripristinarle.";
"settings_key_backup_info_progress" = "Backup di %@ chiavi…";
"settings_key_backup_info_progress_done" = "Backup di tutte le chiavi completato";
"settings_key_backup_info_trust_signature_unknown" = "Il backup ha una firma dal dispositivo con ID: %@";
@ -389,7 +389,7 @@
"settings_key_backup_button_restore" = "Ripristina da backup";
"settings_key_backup_button_delete" = "Elimina backup";
"settings_key_backup_delete_confirmation_prompt_title" = "Elimina backup";
"settings_key_backup_delete_confirmation_prompt_msg" = "Sei sicuro? Perderai i tuoi messaggi cifrati se le tue chiavi non sono salvate correttamente.";
"settings_key_backup_delete_confirmation_prompt_msg" = "Sei sicuro? Se non hai eseguito un Backup delle Chiavi perderai i tuoi messaggi crittografati.";
// Room Details
"room_details_title" = "Dettagli stanza";
"room_details_people" = "Membri";
@ -406,7 +406,7 @@
"room_details_access_section_invited_only" = "Solo le persone che sono state invitate";
"room_details_access_section_anyone_apart_from_guest" = "Chiunque conosca il link della stanza, eccetto gli ospiti";
"room_details_access_section_anyone" = "Chiunque conosca il link della stanza, compresi gli ospiti";
"room_details_access_section_no_address_warning" = "Per indirizzare ad una stanza tramite link, essa deve avere un indirizzo";
"room_details_access_section_no_address_warning" = "Per poter essere linkata, la stanza deve avere un indirizzo";
"room_details_access_section_directory_toggle" = "Mostra questa stanza nell'elenco delle stanze";
"room_details_history_section" = "Chi può leggere la cronologia?";
"room_details_history_section_anyone" = "Chiunque";
@ -414,16 +414,16 @@
"room_details_history_section_members_only_since_invited" = "Solo i membri (dal momento in cui vengono invitati)";
"room_details_history_section_members_only_since_joined" = "Solo i membri (dal momento in cui entrano nella stanza)";
"room_details_history_section_prompt_title" = "Avvertimento sulla privacy";
"room_details_history_section_prompt_msg" = "La leggibilità della cronologia sarà modificata solo per i futuri messaggi di questa stanza. La visibilità dei messaggi esistenti rimarrà invariata.";
"room_details_history_section_prompt_msg" = "I permessi di lettura di questa stanza saranno modificati solo per i messaggi futuri. I permessi di lettura dei messaggi esistenti rimarranno invariati.";
"room_details_addresses_section" = "Indirizzi";
"room_details_no_local_addresses" = "Questa stanza non ha indirizzi locali";
"room_details_new_address" = "Aggiungi nuovo indirizzo";
"room_details_new_address_placeholder" = "Aggiungi nuovo indirizzo (es. #foo%@)";
"room_details_addresses_invalid_address_prompt_title" = "Formato soprannome non valido";
"room_details_addresses_invalid_address_prompt_msg" = "%@ non è un formato valido per un soprannome";
"room_details_addresses_invalid_address_prompt_title" = "Il formato dell'indirizzo non é valido";
"room_details_addresses_invalid_address_prompt_msg" = "%@ non è un formato valido per un alias";
"room_details_addresses_disable_main_address_prompt_title" = "Avviso per l'indirizzo principale";
"room_details_addresses_disable_main_address_prompt_msg" = "Non avrai nessun indirizzo principale specificato. L'indirizzo principale per questa stanza sarà selezionato casualmente";
"room_details_flair_section" = "Mostra insegna per le comunità";
"room_details_addresses_disable_main_address_prompt_msg" = "Non hai specificato un indirizzo principale. L'indirizzo principale per questa stanza sarà selezionato casualmente";
"room_details_flair_section" = "Mostra predisposizione per le comunità";
"room_details_new_flair_placeholder" = "Aggiungi nuovo ID della comunità (es. +foo%@)";
"room_details_flair_invalid_id_prompt_title" = "Formato non valido";
"room_details_flair_invalid_id_prompt_msg" = "%@ non è un identificatore valido per una comunità";
@ -433,23 +433,23 @@
"room_details_advanced_enable_e2e_encryption" = "Abilita crittografia (attenzione: non può più essere disabilitata!)";
"room_details_advanced_e2e_encryption_enabled" = "Crittografia abilitata in questa stanza";
"room_details_advanced_e2e_encryption_disabled" = "Crittografia non abilitata in questa stanza.";
"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Cripta solo per i dispositivi verificati";
"room_details_fail_to_update_avatar" = "Fallita modifica dell'immagine stanza";
"room_details_fail_to_update_room_name" = "Fallita modifica del nome stanza";
"room_details_fail_to_update_topic" = "Fallita modifica dell'argomento";
"room_details_fail_to_update_room_guest_access" = "Fallita modifica dell'accesso agli ospiti alla stanza";
"room_details_fail_to_update_room_join_rule" = "Fallita modifica delle regole di accesso";
"room_details_fail_to_update_room_directory_visibility" = "Fallita modifica della visibilità nell'elenco stanze";
"room_details_fail_to_update_history_visibility" = "Fallita modifica della visibilità della cronologia";
"room_details_fail_to_add_room_aliases" = "Fallita aggiunta dei nuovi indirizzi stanza";
"room_details_fail_to_remove_room_aliases" = "Fallita rimozione degli indirizzi stanza";
"room_details_fail_to_update_room_canonical_alias" = "Fallita modifica dell'indirizzo principale";
"room_details_fail_to_update_room_communities" = "Fallita modifica delle comunità collegate";
"room_details_fail_to_update_room_direct" = "Fallita modifica nel rendere diretta questa stanza";
"room_details_fail_to_enable_encryption" = "Fallita attivazione della cifratura in questa stanza";
"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Crittografa solo per i dispositivi verificati";
"room_details_fail_to_update_avatar" = "La modifica dell'immagine stanza é fallita";
"room_details_fail_to_update_room_name" = "La modifica del nome della stanza é fallita";
"room_details_fail_to_update_topic" = "La modifica dell'argomento é fallita";
"room_details_fail_to_update_room_guest_access" = "La modifica dell'accesso ospiti alla stanza é fallita";
"room_details_fail_to_update_room_join_rule" = "La modifica delle regole di accesso é fallita";
"room_details_fail_to_update_room_directory_visibility" = "la modifica della visibilità nell'elenco stanze é fallita";
"room_details_fail_to_update_history_visibility" = "La modifica della visibilità della cronologia é fallita";
"room_details_fail_to_add_room_aliases" = "L'ggiunta dei nuovi indirizzi stanza é fallita";
"room_details_fail_to_remove_room_aliases" = "La rimozione degli indirizzi stanza é fallita";
"room_details_fail_to_update_room_canonical_alias" = "La modifica dell'indirizzo principale é fallita";
"room_details_fail_to_update_room_communities" = "La modifica delle comunità collegate é fallita";
"room_details_fail_to_update_room_direct" = "La modifica di questa stanza in Chat Diretta é fallita";
"room_details_fail_to_enable_encryption" = "L'attivazione della crittografia in questa stanza é fallita";
"room_details_save_changes_prompt" = "Vuoi salvare le modifiche?";
"room_details_set_main_address" = "Imposta come indirizzo principale";
"room_details_unset_main_address" = "Non più come indirizzo principale";
"room_details_unset_main_address" = "Non utilizzare più come indirizzo principale";
"room_details_copy_room_id" = "Copia ID stanza";
"room_details_copy_room_address" = "Copia indirizzo stanza";
"room_details_copy_room_url" = "Copia URL stanza";
@ -466,31 +466,31 @@
"group_invitation_format" = "%@ ti ha invitato ad unirti a questa comunità";
// Group participants
"group_participants_add_participant" = "Aggiungi membri";
"group_participants_leave_prompt_title" = "Lascia stanza";
"group_participants_leave_prompt_title" = "Lascia la stanza";
"group_participants_leave_prompt_msg" = "Sei sicuro di voler uscire dalla stanza?";
"group_participants_remove_prompt_title" = "Conferma";
"group_participants_remove_prompt_msg" = "Sei sicuro di voler rimuovere %@ da questa chat?";
"group_participants_invite_prompt_title" = "Conferma";
"group_participants_invite_prompt_msg" = "Sei sicuro di voler invitare %@ in questa stanza?";
"group_participants_filter_members" = "Filtra membri comunità";
"group_participants_filter_members" = "Filtra i membri della comunità";
"group_participants_invite_another_user" = "Cerca / invita per ID utente o nome";
"group_participants_invite_malformed_id_title" = "Invito fallito";
"group_participants_invite_malformed_id" = "ID malformato. Dovrebbe essere ID Matrix come '@localpart:domain'";
"group_participants_invited_section" = "INVITATI";
// Group rooms
"group_rooms_filter_rooms" = "Filtra stanze comunità";
"group_rooms_filter_rooms" = "Filtra le stanze della comunità";
// Read Receipts
"read_receipts_list" = "Elenco ricevute di lettura";
"receipt_status_read" = "Letto: ";
// Media picker
"media_picker_library" = "Galleria";
"media_picker_library" = "Galleria Immagini";
"media_picker_select" = "Seleziona";
// Directory
"directory_title" = "Elenco";
"directory_server_picker_title" = "Seleziona un elenco";
"directory_server_all_rooms" = "Tutte le stanze sul server %@";
"directory_server_all_native_rooms" = "Tutte le stanze Matrix native";
"directory_server_type_homeserver" = "Digita un server home per elencare le sue stanze pubbliche";
"directory_server_type_homeserver" = "Digita un Homeserver per vedere l'elenco delle sue stanze pubbliche";
"directory_server_placeholder" = "matrix.org";
// Events formatter
"event_formatter_member_updates" = "%tu cambi d'appartenenza";
@ -498,20 +498,20 @@
"event_formatter_widget_removed" = "%@ widget rimosso da %@";
"event_formatter_jitsi_widget_added" = "Conferenza VoIP aggiunta da %@";
"event_formatter_jitsi_widget_removed" = "Conferenza VoIP rimossa da %@";
"event_formatter_rerequest_keys_part1_link" = "Richiedi di nuovo le chiavi di cifratura";
"event_formatter_rerequest_keys_part1_link" = "Richiedi nuovamente le chiavi di crittografia";
"event_formatter_rerequest_keys_part2" = " dai tuoi altri dispositivi.";
// Others
"or" = "o";
"you" = "Tu";
"today" = "Oggi";
"yesterday" = "Ieri";
"network_offline_prompt" = "La connessione a internet sembra essere disconnessa.";
"homeserver_connection_lost" = "Non riesco a connettermi al server home.";
"network_offline_prompt" = "La connessione a internet sembra essere assente.";
"homeserver_connection_lost" = "Non riesco a connettermi all'Homeserver.";
"public_room_section_title" = "Stanze pubbliche (in %@):";
"bug_report_prompt" = "L'applicazione è andata in crash l'ultima volta. Desideri aprire la schermata di segnalazione del crash?";
"rage_shake_prompt" = "Sembra che tu stia scuotendo il dispositivo con frustrazione. Desideri aprire la schermata di segnalazione errore?";
"rage_shake_prompt" = "Sembra che tu stia scuotendo il dispositivo con rabbia. Vuoi aprire la schermata di segnalazione errore?";
"do_not_ask_again" = "Non chiedere più";
"camera_access_not_granted" = "%@ non ha il permesso per usare la fotocamera, modifica le impostazioni privacy";
"camera_access_not_granted" = "%@ non ha il permesso di usare la fotocamera. Modifica le impostazioni di privacy";
"large_badge_value_k_format" = "%.1fK";
"room_does_not_exist" = "%@ non esiste";
// Call
@ -519,19 +519,19 @@
"call_incoming_video_prompt" = "Videochiamata in arrivo da %@";
"call_incoming_voice" = "Chiamata in arrivo...";
"call_incoming_video" = "Videochiamata in arrivo...";
"call_already_displayed" = "Già presente una chiamata in corso.";
"call_jitsi_error" = "Fallito ingresso alla chiamata di conferenza.";
"call_already_displayed" = "C'é già una chiamata in corso.";
"call_jitsi_error" = "L'accesso alla chiamata in conferenza é fallito.";
// No VoIP support
"no_voip_title" = "Chiamata in arrivo";
"no_voip" = "%@ ti sta chiamando, ma %@ non supporta ancora le chiamate.\nPuoi ignorare questa notifica e rispondere alla chiamata da un altro dispositivo o puoi rifiutare la chiamata.";
"no_voip" = "%@ ti sta chiamando, ma %@ non supporta ancora le chiamate.\nPuoi ignorare questa notifica e rispondere alla chiamata da un altro dispositivo oppure puoi rifiutare la chiamata.";
// Crash report
"google_analytics_use_prompt" = "Vuoi aiutare a migliorare %@ inviando automaticamente in modo anonimo i dati di utilizzo e le segnalazioni di crash?";
// Crypto
"e2e_enabling_on_app_update" = "Riot ora supporta la crittografia da-utente-a-utente ma devi rieseguire l'accesso per abilitarla.\n\nPuoi farlo adesso oppure dopo dalle impostazioni dell'applicazione.";
"e2e_need_log_in_again" = "È necessario eseguire nuovamente l'accesso per generare le chiavi di crittografia da-utente-a-utente per questo dispositivo ed inviare la chiave pubblica al server home.\nCiò può capitare solo una volta; ci scusiamo per l'inconveniente.";
"e2e_enabling_on_app_update" = "Riot ora supporta la crittografia da-utente-a-utente ma devi eseguire nuovamente l'accesso per abilitarla.\n\nPuoi farlo ora o più tardi dalle impostazioni dell'applicazione.";
"e2e_need_log_in_again" = "È necessario eseguire nuovamente l'accesso per generare le chiavi di crittografia da-utente-a-utente per questo dispositivo ed inviare la chiave pubblica all'Homeserver.\nVa fatto una sola volta; scusa per il disturbo.";
// Key backup wrong version
"e2e_key_backup_wrong_version_title" = "Nuovo backup chiave";
"e2e_key_backup_wrong_version" = "È stato rilevato un nuovo backup chiavi per messaggi sicuri. \n\nSe non sei stato tu, imposta una nuova passphrase dalle impostazioni.";
"e2e_key_backup_wrong_version_title" = "Nuovo Backup delle chiavi";
"e2e_key_backup_wrong_version" = "È stato effettuato un nuovo Backup delle chiavi per i messaggi crittografati. \n\nSe non sei stato tu, imposta una nuova frase di sicurezza dalle impostazioni.";
"e2e_key_backup_wrong_version_button_settings" = "Impostazioni";
"e2e_key_backup_wrong_version_button_wasme" = "Sono stato io";
// Bug report
@ -548,14 +548,14 @@
// Widget
"widget_no_power_to_manage" = "Hai bisogno dei permessi per gestire i widget in questa stanza";
"widget_creation_failure" = "Creazione del widget fallita";
"widget_sticker_picker_no_stickerpacks_alert" = "Non hai ancora alcun pacco di adesivi attivo.";
"widget_sticker_picker_no_stickerpacks_alert" = "Non hai nessun pacchetto sticker attivo.";
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Aggiungerne qualcuno ora?";
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "Devi poter inviare utenti per compiere questa azione.";
"widget_integration_need_to_be_able_to_invite" = "Devi poter invitare utenti per compiere questa azione.";
"widget_integration_unable_to_create" = "Impossibile creare il widget.";
"widget_integration_failed_to_send_request" = "Invio della richiesta fallito.";
"widget_integration_room_not_recognised" = "Questa stanza non è riconosciuta.";
"widget_integration_positive_power_level" = "Il livello di potere deve essere un intero positivo.";
"widget_integration_positive_power_level" = "Il livello dei permessi deve avere un valore intero e positivo.";
"widget_integration_must_be_in_room" = "Non sei in questa stanza.";
"widget_integration_no_permission_in_room" = "Non hai i permessi per eseguire l'azione in questa stanza.";
"widget_integration_missing_room_id" = "room_id mancante nella richiesta.";
@ -572,15 +572,15 @@
"e2e_room_key_request_share_without_verifying" = "Condividi senza verificare";
"e2e_room_key_request_ignore_request" = "Ignora richiesta";
// GDPR
"gdpr_consent_not_given_alert_message" = "Per continuare ad usare l'homeserver %@ devi leggere e accettare i termini e le condizioni di utilizzo.";
"gdpr_consent_not_given_alert_message" = "Per continuare ad usare l'Homeserver %@ devi leggere e accettare i termini e le condizioni di utilizzo.";
"gdpr_consent_not_given_alert_review_now_action" = "Leggi adesso";
"deactivate_account_title" = "Disattiva account";
"deactivate_account_informations_part1" = "Questo renderà il tuo account permanentemente inutilizzabile. Non ci potrai più accedere e nessuno potrà ri-registrare lo stesso ID utente. Il tuo account abbandonerà tutte le stanze a cui partecipa e i dettagli del tuo account saranno rimossi dal server identità. ";
"deactivate_account_informations_part1" = "Questo renderà il tuo account inutilizzabile per sempre. Non ci potrai più accedere e nessuno potrà riutilizzare questo ID utente. Il tuo account abbandonerà tutte le stanze a cui partecipa e i dettagli del tuo account saranno rimossi dall'Identity Server. ";
"deactivate_account_informations_part2_emphasize" = "Questa azione è irreversibile.";
"deactivate_account_informations_part3" = "\n\nDisattivare il tuo account ";
"deactivate_account_informations_part4_emphasize" = "non eliminerà in modo automatico i messaggi che hai inviato. ";
"deactivate_account_informations_part5" = "Se vuoi che siano dimenticati i tuoi messaggi, seleziona la casella qua sotto\n\nLa visibilità dei messaggi in Matrix è simile alle email. \"Dimenticare i tuoi messaggi\" significa che quelli che hai inviato non verranno condivisi con alcun utente nuovo o non registrato, ma gli utenti registrati che hanno avuto accesso ai tuoi messaggi avranno ancora accesso alla loro copia.";
"deactivate_account_forget_messages_information_part1" = "Per favore dimenticate tutti i messaggi che ho inviato al momento della disattivazione del mio account (";
"deactivate_account_informations_part5" = "Se vuoi che i tuoi messaggi siano dimenticati, seleziona la casella qui sotto\n\nLa visibilità dei messaggi in Matrix è simile alle email: \"dimenticare i tuoi messaggi\" significa che quelli che hai già inviato non verranno condivisi con nessun utente nuovo o non registrato, ma gli utenti registrati che già hanno avuto accesso ai tuoi messaggi potranno ancora accedere alla loro copia.";
"deactivate_account_forget_messages_information_part1" = "Dal momento in cui disattivo il mio account dimentica tutti i messaggi che ho inviato in precedenza (";
"deactivate_account_forget_messages_information_part2_emphasize" = "Attenzione";
"deactivate_account_forget_messages_information_part3" = ": gli utenti futuri vedranno un elenco incompleto di conversazioni)";
"deactivate_account_validate_action" = "Disattiva account";
@ -588,32 +588,32 @@
"deactivate_account_password_alert_message" = "Per proseguire, inserisci la tua password";
// Re-request confirmation dialog
"rerequest_keys_alert_title" = "Richiesta inviata";
"rerequest_keys_alert_message" = "Avvia Riot su un altro dispositivo che possa decifrare il messaggio, in modo da inviare le chiavi a questo dispositivo.";
"rerequest_keys_alert_message" = "Avvia Riot su un altro dispositivo che possa decrittare il messaggio, in modo da poter inviare le chiavi a questo dispositivo.";
"key_backup_setup_title" = "Backup chiave";
"key_backup_setup_skip_alert_title" = "Sei sicuro?";
"key_backup_setup_skip_alert_message" = "Potresti perdere i tuoi messaggi cifrati se ti disconnetti o perdi il dispositivo.";
"key_backup_setup_skip_alert_message" = "Se ti disconnetti oppure perdi il dispositivo potresti perdere i tuoi messaggi crittografati.";
"key_backup_setup_skip_alert_skip_action" = "Salta";
"key_backup_setup_intro_title" = "Non perdere mai i messaggi cifrati";
"key_backup_setup_intro_info" = "I messaggi nelle stanze cifrate sono protetti con la cifratura end-to-end. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi. \n \nFai un backup sicuro delle tue chiavi per evitare di perderle.";
"key_backup_setup_intro_title" = "Non perdere mai i messaggi crittografati";
"key_backup_setup_intro_info" = "I messaggi nelle stanze crittografate sono protetti con crittografia da-utente-a-utente. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi. \n \nFai un backup delle tue chiavi per evitare di perderle.";
"key_backup_setup_intro_setup_action_without_existing_backup" = "Inizia ad usare il backup chiavi";
"key_backup_setup_intro_manual_export_info" = "(Avanzato)";
"key_backup_setup_intro_manual_export_action" = "Esporta manualmente le chiavi";
"key_backup_setup_passphrase_title" = "Proteggi il tuo backup con una frase d'accesso";
"key_backup_setup_passphrase_info" = "Salveremo una copia cifrata delle tue chiavi nel tuo homeserver. Proteggi il tuo backup con una frase d'accesso (passphrase) per tenerlo sicuro. \n \nPer una massima sicurezza, dovrebbe essere diversa dalla password del tuo account.";
"key_backup_setup_passphrase_info" = "Salveremo una copia crittografata delle tue chiavi nel tuo Homeserver. Proteggi il tuo backup con una frase di sicurezza per tenerlo sicuro. \n \nPer una massima sicurezza, dovrebbe essere diversa dalla password del tuo account.";
"key_backup_setup_passphrase_passphrase_title" = "Inserisci";
"key_backup_setup_passphrase_passphrase_placeholder" = "Inserisci passphrase";
"key_backup_setup_passphrase_passphrase_placeholder" = "Inserisci frase d'accesso";
"key_backup_setup_passphrase_passphrase_valid" = "Bene!";
"key_backup_setup_passphrase_passphrase_invalid" = "Prova ad aggiungere una parola";
"key_backup_setup_passphrase_confirm_passphrase_title" = "Conferma";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Conferma passphrase";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Conferma frase d'accesso";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "Bene!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Le passphrase devono corrispondere";
"key_backup_setup_passphrase_set_passphrase_action" = "Imposta passphrase";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Oppure, proteggi il tuo backup con una chiave di ripristino, salvandola in un luogo sicuro.";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Le frasi d'accesso devono corrispondere";
"key_backup_setup_passphrase_set_passphrase_action" = "Imposta frase d'accesso";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Oppure proteggi il tuo backup con una chiave di ripristino, salvandola in un luogo sicuro.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Avanzato) Imposta con chiave di ripristino";
"key_backup_setup_success_title" = "Completato!";
// Success from passphrase
"key_backup_setup_success_from_passphrase_info" = "Backup delle tue chiavi in corso.\n\nLa tua chiave di ripristino è una rete di sicurezza - puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati se dimentichi la tua passphrase. \n\nTieni la tua chiave di ripristino in un luogo sicuro, come un password manager (o una cassaforte).";
"key_backup_setup_success_from_passphrase_info" = "Backup delle tue chiavi in corso.\n\nLa tua chiave di ripristino è uno strumento di sicurezza - puoi usarla per recuperare l'accesso ai tuoi messaggi crittografati se dimentichi la tua frase d'accesso. \n\nTieni la tua chiave di ripristino in un luogo sicuro, come un password manager (o una cassaforte).";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Salva chiave di ripristino";
"key_backup_setup_success_from_passphrase_done_action" = "Fatto";
// Success from recovery key
@ -622,15 +622,15 @@
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Fai una copia";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Ho fatto una copia";
"key_backup_recover_title" = "Messaggi sicuri";
"key_backup_recover_invalid_passphrase_title" = "Passphrase di ripristino non corretta";
"key_backup_recover_invalid_passphrase" = "Impossibile decifrare il backup con questa passphrase: verifica di avere inserito la passphrase di ripristino corretta.";
"key_backup_recover_invalid_passphrase_title" = "Frase di sicurezza non corretta";
"key_backup_recover_invalid_passphrase" = "Impossibile decifrare il backup con questa frase di sicurezza. Verifica di avere inserito la frase di sicurezza corretta.";
"key_backup_recover_invalid_recovery_key_title" = "La chiave di ripristino non corrisponde";
"key_backup_recover_invalid_recovery_key" = "Impossibile decifrare il backup con questa chiave di ripristino: verifica di avere inserito la chiave di ripristino corretta.";
"key_backup_recover_from_passphrase_info" = "Usa la tua chiave di ripristino per sbloccare la cronologia dei messaggi cifrati";
"key_backup_recover_invalid_recovery_key" = "Impossibile decrittare il backup con questa chiave di recupero: verifica che la chiave di recupero sia corretta.";
"key_backup_recover_from_passphrase_info" = "Usa la tua chiave di ripristino per sbloccare la cronologia dei messaggi crittografati";
"key_backup_recover_from_passphrase_passphrase_title" = "Inserisci";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Inserisci passphrase";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Inserisci frase d'accesso";
"key_backup_recover_from_passphrase_recover_action" = "Sblocca cronologia";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Non conosci la tua passphrase di ripristino? Puoi ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Non conosci la tua frase d'accesso? Puoi ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "usa la tua chiave di ripristino";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
"key_backup_recover_from_recovery_key_info" = "Usa la tua chiave di ripristino per sbloccare la cronologia dei tuoi messaggi sicuri";
@ -640,18 +640,249 @@
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Hai perso la chiave di ripristino? Puoi crearne una nuova nelle impostazioni.";
"key_backup_recover_success_info" = "Backup ripristinato!";
"key_backup_recover_done_action" = "Fatto";
"key_backup_setup_banner_title" = "Non perdere mai i messaggi cifrati";
"key_backup_setup_banner_title" = "Non perdere mai i messaggi crittografati";
"key_backup_setup_banner_subtitle" = "Inizia ad usare il backup chiavi";
"key_backup_recover_banner_title" = "Non perdere mai i messaggi cifrati";
"key_backup_recover_banner_title" = "Non perdere mai i messaggi crittografati";
"sign_out_existing_key_backup_alert_title" = "Sei sicuro di volerti disconnettere?";
"sign_out_existing_key_backup_alert_sign_out_action" = "Disconnetti";
"sign_out_non_existing_key_backup_alert_title" = "Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi cifrati";
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Inizia ad usare il backup chiavi";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Non voglio i miei messaggi cifrati";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Perderai i tuoi messaggi cifrati";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Perderai l'accesso ai tuoi messaggi cifrati a meno che non fai il backup delle tue chiavi prima di disconnetterti.";
"sign_out_non_existing_key_backup_alert_title" = "Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi crittografati";
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Inizia ad usare il Backup delle chiavi";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Non voglio i miei messaggi crittografati";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Perderai i tuoi messaggi crittografati";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Perderai l'accesso ai tuoi messaggi crittografati a meno che non fai il Backup delle tue chiavi prima di disconnetterti.";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Disconnetti";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Backup";
"sign_out_key_backup_in_progress_alert_title" = "Backup chiave in corso. Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi cifrati.";
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Non voglio i miei messaggi cifrati";
"sign_out_key_backup_in_progress_alert_cancel_action" = "Aspetto";
"sign_out_key_backup_in_progress_alert_title" = "Backup chiavi in corso. Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi crittografati.";
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Non voglio i miei messaggi crittografati";
"sign_out_key_backup_in_progress_alert_cancel_action" = "Attendo";
"close" = "Chiudi";
"auth_forgot_password_error_no_configured_identity_server" = "Nessun identità server è configurata: aggiungine uno per reimpostare la password.";
"auth_softlogout_signed_out" = "Sei uscito";
"auth_softlogout_sign_in" = "Accedi";
"auth_softlogout_reason" = "L'amministratore homeserver (%1$@) ti ha disconnesso dal tuo account %2$@ (%3$@).";
"auth_softlogout_recover_encryption_keys" = "Accedi per recuperare le chiavi di crittografia archiviate esclusivamente su questo dispositivo. Ti servono per leggere tutti i tuoi messaggi sicuri su qualsiasi dispositivo.";
"auth_softlogout_clear_data" = "Cancella dati personali";
"auth_softlogout_clear_data_message_1" = "Avviso: i tuoi dati personali (comprese le chiavi di crittografia) sono ancora memorizzati su questo dispositivo.";
"auth_softlogout_clear_data_message_2" = "Cancellalo se hai finito di utilizzare questo dispositivo o desideri accedere a un altro account.";
"auth_softlogout_clear_data_button" = "Cancella tutti i dati";
"auth_softlogout_clear_data_sign_out_title" = "Sei sicuro?";
"auth_softlogout_clear_data_sign_out_msg" = "Sei sicuro di voler cancellare tutti i dati attualmente memorizzati su questo dispositivo? Accedi di nuovo per accedere ai dati e ai messaggi del tuo account.";
"auth_softlogout_clear_data_sign_out" = "disconnessione";
"room_creation_error_invite_user_by_email_without_identity_server" = "Nessuna identità server è configurata, quindi non è possibile aggiungere un partecipante con un'e-mail.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Non è stato configurato alcun Identity Server, quindi non è possibile avviare una chat con un contatto tramite e-mail.";
"room_event_action_reply" = "Rispondi";
"room_event_action_edit" = "Modifica";
"room_event_action_reaction_show_all" = "Mostra tutto";
"room_event_action_reaction_show_less" = "Mostra meno";
"room_event_action_reaction_history" = "Cronologia reazioni";
"room_action_camera" = "Scatta foto o video";
"room_action_send_file" = "Invia File";
"room_action_reply" = "Rispondi";
"room_message_edits_history_title" = "Modifica messaggio";
"settings_labs_message_reaction" = "Reagisci ai messaggi con emoji";
"room_participants_remove_third_party_invite_prompt_msg" = "Sei sicuro di voler revocare l'invito?";
"settings_key_backup_button_connect" = "Connetti questo dispositivo al Backup delle Chiavi";
// Media picker
"media_picker_title" = "Media library";
// Image picker
"image_picker_action_camera" = "Scatta foto";
"image_picker_action_library" = "Seleziona dalla Libreria Immagini";
"event_formatter_message_edited_mention" = "(modificato)";
"camera_unavailable" = "La Fotocamera su questo dispositivo non é disponibile";
"photo_library_access_not_granted" = "%@ non ha il permesso di accedere alla Photo Library. Modifica le impostazioni di privacy";
// Widget
"widget_no_integrations_server_configured" = "Il Server Integrazioni non é stato configurato";
"widget_integrations_server_failed_to_connect" = "La connessione al Server Integrazioni é fallita";
// Service terms
"service_terms_modal_title" = "Termini del servizio";
"service_terms_modal_message" = "Per continuare devi accettare i Termini di servizio (%@).";
"service_terms_modal_accept_button" = "Accetta";
"service_terms_modal_description_for_identity_server" = "Gli altri utenti possono trovarti";
"service_terms_modal_description_for_integration_manager" = "Usa bot, widget e stickers";
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Connetti questo dispositivo al Backup delle chiavi";
"key_backup_recover_connent_banner_subtitle" = "Connetti questo dispositivo al Backup delle chiavi";
// MARK: - Device Verification
"device_verification_title" = "Verifica dispositivo";
"device_verification_security_advice" = "Per avere la massima sicurezza ti consigliamo di farlo di persona o utilizzando un altro sistema di comunicazione sicura";
"device_verification_cancelled" = "L'altro utente/dispositivo ha annullato la verifica.";
"device_verification_cancelled_by_me" = "La verifica é stata cancellata. Motivo: %@";
"device_verification_error_cannot_load_device" = "Non si riescono a recuperare le informazioni riguardanti il dipositivo.";
// Mark: Incoming
"device_verification_incoming_title" = "Richiesta di verifica in corso";
"device_verification_incoming_description_1" = "Verifica questo dispositivo per contrassegnarlo come fidato. Verificare che i dispositivi dei tuoi contatti siano fidati dà maggiore tranquillità quando usi messaggi crittografati da-utente-a-utente con loro.";
"device_verification_incoming_description_2" = "Effettuando la verifica, il tuo dispositivo e quello del tuo contatto verranno reciprocamente contrassegnati come fidati.";
// MARK: Start
"device_verification_start_title" = "Verifica confrontando un breve testo";
"device_verification_start_wait_partner" = "Attendi che il tuo contatto accetti...";
"device_verification_start_use_legacy" = "Non accade nulla? Non tutti i dispositivi supportano la verifica interattiva. Usa la verifica classica.";
"device_verification_start_verify_button" = "Inizia verifica";
"device_verification_start_use_legacy_action" = "Usa verifica Legacy";
// MARK: Verify
"device_verification_verify_title_emoji" = "Verifica questo dispositivo confermando che i seguenti emoji appaiono sullo schermo del tuo contatto";
"device_verification_verify_title_number" = "Verifica questo dispositivo confermando che il seguente numero appare sullo schermo del tuo contatto";
"device_verification_verify_wait_partner" = "In attesa che il tuo contatto confermi...";
// MARK: Verified
"device_verification_verified_title" = "Verificato!";
"device_verification_verified_description_1" = "Hai verificato correttamente questo dispositivo.";
"device_verification_verified_description_2" = "Rendi sicuri i tuoi messaggi con questo contatto usando la crittografia da-utente-a-utente in modo che nessun'altro possa leggerli.";
"device_verification_verified_got_it_button" = "Fatto";
// MARK: Emoji
"device_verification_emoji_dog" = "Cane";
"device_verification_emoji_cat" = "Gatto";
"device_verification_emoji_lion" = "Leone";
"device_verification_emoji_horse" = "Cavallo";
"device_verification_emoji_unicorn" = "Unicorno";
"device_verification_emoji_pig" = "Maiale";
"device_verification_emoji_elephant" = "Elefante";
"device_verification_emoji_rabbit" = "Coniglio";
"device_verification_emoji_panda" = "Panda";
"device_verification_emoji_rooster" = "Gallo";
"device_verification_emoji_penguin" = "Pinguino";
"device_verification_emoji_turtle" = "Tartaruga";
"device_verification_emoji_fish" = "Pesce";
"device_verification_emoji_octopus" = "Polipo";
"device_verification_emoji_butterfly" = "Farfalla";
"device_verification_emoji_flower" = "Fiore";
"device_verification_emoji_tree" = "Albero";
"device_verification_emoji_cactus" = "Cactus";
"device_verification_emoji_mushroom" = "Fungo";
"device_verification_emoji_globe" = "Terra";
"device_verification_emoji_moon" = "Luna";
"device_verification_emoji_cloud" = "Nuvola";
"device_verification_emoji_fire" = "Fuoco";
"device_verification_emoji_banana" = "Banana";
"device_verification_emoji_apple" = "Mela";
"device_verification_emoji_strawberry" = "Fragola";
"device_verification_emoji_corn" = "Pannocchia";
"device_verification_emoji_pizza" = "Pizza";
"device_verification_emoji_cake" = "Torta";
"device_verification_emoji_heart" = "Cuore";
"device_verification_emoji_smiley" = "Sorriso";
"device_verification_emoji_robot" = "Robot";
"device_verification_emoji_hat" = "Cappello";
"device_verification_emoji_glasses" = "Occhiali";
"device_verification_emoji_spanner" = "Chiave inglese";
"device_verification_emoji_santa" = "Babbo Natale";
"device_verification_emoji_thumbs up" = "Pollice su";
"device_verification_emoji_umbrella" = "Ombrello";
"device_verification_emoji_hourglass" = "Clessidra";
"device_verification_emoji_clock" = "Orologio";
"device_verification_emoji_gift" = "Regalo";
"device_verification_emoji_light bulb" = "Lampadina";
"device_verification_emoji_book" = "Libro";
"device_verification_emoji_pencil" = "Matita";
"device_verification_emoji_paperclip" = "Graffetta";
"device_verification_emoji_scissors" = "Forbici";
"device_verification_emoji_lock" = "Lucchetto";
"device_verification_emoji_key" = "Chiave";
"device_verification_emoji_hammer" = "Martello";
"device_verification_emoji_telephone" = "Telefono";
"device_verification_emoji_flag" = "Bandiera";
"device_verification_emoji_train" = "Treno";
"device_verification_emoji_bicycle" = "Bicicletta";
"device_verification_emoji_aeroplane" = "Aeroplano";
"device_verification_emoji_rocket" = "Razzo";
"device_verification_emoji_trophy" = "Coppa";
"device_verification_emoji_ball" = "Palla";
"device_verification_emoji_guitar" = "Chitarra";
"device_verification_emoji_trumpet" = "Tromba";
"device_verification_emoji_bell" = "Campana";
"device_verification_emoji_anchor" = "Ancora";
"device_verification_emoji_headphones" = "Cuffie";
"device_verification_emoji_folder" = "Cartella";
"device_verification_emoji_pin" = "Puntina";
// MARK: File upload
"file_upload_error_title" = "Caricamento File";
"file_upload_error_unsupported_file_type_message" = "Tipo di File non supportato.";
// MARK: Emoji picker
"emoji_picker_title" = "Reazioni";
"emoji_picker_people_category" = "Smiley & Persone";
"emoji_picker_nature_category" = "Animali & Natura";
"emoji_picker_foods_category" = "Cibo & Bevande";
"emoji_picker_activity_category" = "Attività";
"emoji_picker_places_category" = "Viaggi & Luoghi";
"emoji_picker_objects_category" = "Oggetti";
"emoji_picker_symbols_category" = "Simboli";
"emoji_picker_flags_category" = "Bandiere";
// MARK: Reaction history
"reaction_history_title" = "Reazioni";
// Errors
"error_user_already_logged_in" = "Sembra tu stia tentando di connetterti ad un altro Homeserver. Vuoi disconnetterti?";
"room_accessiblity_scroll_to_bottom" = "Scorri in fondo";
"room_accessibility_search" = "Cerca";
"room_accessibility_integrations" = "Integrazioni";
"room_accessibility_upload" = "Invia";
"room_accessibility_call" = "Chiama";
"room_accessibility_hangup" = "Riaggancia";
"media_type_accessibility_image" = "Immagine";
"media_type_accessibility_audio" = "Audio";
"media_type_accessibility_video" = "Video";
"media_type_accessibility_location" = "Posizione";
"media_type_accessibility_file" = "File";
"media_type_accessibility_sticker" = "Adesivo";
// Widget Picker
"widget_picker_title" = "Integrazioni";
"auth_add_email_message_2" = "Imposta un'email per il recupero dell'account, più tardi anche per essere trovabile dalle persone che ti conoscono.";
"auth_add_phone_message_2" = "Imposta un telefono, più tardi anche per essere trovabile dalle persone che ti conoscono.";
"auth_add_email_phone_message_2" = "Imposta un'email per il recupero dell'account. Più tardi usa l'email o il telefono per essere trovabile dalle persone che ti conoscono.";
"auth_email_is_required" = "Nessun server di identità configurato, perciò non puoi aggiungere un indirizzo email per ripristinare la password in futuro.";
"auth_phone_is_required" = "Nessun server di identità configurato, perciò non puoi aggiungere un numero di telefono per ripristinare la password in futuro.";
"auth_reset_password_error_is_required" = "Nessun server di identità configurato: aggiungine uno nelle opzioni server per ripristinare la password.";
"contacts_address_book_no_identity_server" = "Nessun server di identità configurato";
"settings_discovery_settings" = "SCOPRI";
"settings_identity_server_settings" = "SERVER IDENTITÀ";
"settings_three_pids_management_information_part1" = "Gestisci qui quali indirizzi email o numeri di telefono puoi usare per accedere o recuperare l'account. Controlla chi può trovarti in ";
"settings_three_pids_management_information_part2" = "Scopri";
"settings_three_pids_management_information_part3" = ".";
"settings_calls_stun_server_fallback_button" = "Permetti server di assistenza alle chiamate di fallback";
"settings_calls_stun_server_fallback_description" = "Consenti server di assistenza alle chiamate di fallback %@ quando il tuo homeserver non ne offre uno (il tuo indirizzo IP verrà condiviso durante una chiamata).";
"settings_devices_description" = "Il nome pubblico di un dispositivo è visibile dalle persone con cui comunichi";
"settings_discovery_no_identity_server" = "Attualmente non stai usando un server di identità. Per essere trovabile dai contatti esistenti che conosci, aggiungine uno.";
"settings_discovery_terms_not_signed" = "Accetta le condizioni di servizio del server di identità (%@) per poter essere trovabile tramite indirizzo email o numero di telefono.";
"settings_discovery_three_pids_management_information_part1" = "Gestisci quali indirizzi email o numeri di telefono gli altri utenti possono usare per trovarti e invitarti nelle stanze. Aggiungi o rimuovi indirizzi email o numeri di telefono da questa lista in ";
"settings_discovery_three_pids_management_information_part2" = "Impostazioni utente";
"settings_discovery_three_pids_management_information_part3" = ".";
"settings_discovery_error_message" = "C'è stato un errore. Riprova.";
"settings_discovery_three_pid_details_title_email" = "Gestisci email";
"settings_discovery_three_pid_details_information_email" = "Gestisci le impostazioni di questo indirizzo email che altri utenti possono usare per trovarti o invitarti in altre stanze. Aggiungi o rimuovi gli indirizzi email in Accounts.";
"settings_discovery_three_pid_details_title_phone_number" = "Gestisci il numero di telefono";
"settings_discovery_three_pid_details_information_phone_number" = "Gestisci le impostazioni di questo numero di telefono che altri utenti possono usare per trovarti o invitarti in altre stanze. Aggiungi o rimuovi i numeri di telefono in Accounts.";
"settings_discovery_three_pid_details_share_action" = "Condividi";
"settings_discovery_three_pid_details_revoke_action" = "Cancella";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "Annulla la validazione dell'email";
"settings_discovery_three_pid_details_enter_sms_code_action" = "Inserisci il codice d'attivazione SMS";
"settings_identity_server_description" = "Utilizzando l'Identity Server impostato potrai trovare e farti trovare dai contatti esistenti.";
"settings_identity_server_no_is" = "Nessun Identity Server configurato";
"settings_identity_server_no_is_description" = "Non stai usando alcun Identity Server. Selezionane uno per trovare e farti trovare dai contatti esistenti.";
// Identity server settings
"identity_server_settings_title" = "// Impostazioni dell'Identity Server";
"identity_server_settings_description" = "Per trovare e farti trovare dai tuoi contatti stai usando %@.";
"identity_server_settings_no_is_description" = "Non stai usando alcun Identity Server. Selezionane uno per trovare e farti trovare dai contatti esistenti.";
"identity_server_settings_place_holder" = "Inserisci un Identity Server";
"identity_server_settings_add" = "Aggiungi";
"identity_server_settings_change" = "Cambia";
"identity_server_settings_disconnect_info" = "Usare un Identity Server è facoltativo. Se scegli di non usarne uno gli altri utenti non potranno trovarti e tu non potrai invitarli per email o telefono.";
"identity_server_settings_disconnect" = "Disconnetti";
"identity_server_settings_alert_no_terms_title" = "L'Identity Server non ha Termini di servizio";
"identity_server_settings_alert_no_terms" = "L'Identity Server che hai scelto non ha Termini di servizio. Prosegui solo se ti fidi del gestore del server.";
"identity_server_settings_alert_change_title" = "Cambia Identity Server";
"identity_server_settings_alert_change" = "Vuoi scollegarti dall'Identyty Server %1$@ e connetterti invece a %2$@?";
"identity_server_settings_alert_disconnect_title" = "Scollega dall'Identity Server";
"identity_server_settings_alert_disconnect" = "Vuoi scollegarti dall'Identity Server %@?";
"identity_server_settings_alert_disconnect_button" = "Scollega";
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "L'Identity Server %@ può ancora condividere le tue informazioni personali.\n\nTi raccomandiamo di rimuovere tutti i tuoi indirizzi email e numeri di telefono dall'Identity Server prima di scollegarti.";
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Scollegati comunque";
"identity_server_settings_alert_error_terms_not_accepted" = "Devi accettare le condizioni di %@ per impostarlo come tuo Identity Server.";
"identity_server_settings_alert_error_invalid_identity_server" = "%@ non é un Identity Server valido.";
"call_no_stun_server_error_title" = "Chiamata fallita a causa di una configurazione errata del server";
"call_no_stun_server_error_message_1" = "Chiedi all'amministratore del tuo Homeserver %@ di configurare un server TURN in modo che le chiamate funzionino come si deve.";
"call_no_stun_server_error_message_2" = "In alternativa puoi provare ad usare il server pubblico a %@, ma non é molto affidabile e il tuo indirizzo IP verrà condiviso con tale server. Puoi gestire questa cosa nelle Impostazioni";
"call_no_stun_server_error_use_fallback_button" = "Prova ad usare %@";
"service_terms_modal_decline_button" = "Rifiuta";
"service_terms_modal_description_for_identity_server_1" = "Trova altri utenti attraverso l'email o il numero di telefono";
"service_terms_modal_description_for_identity_server_2" = "Fatti trovare attraverso il tuo numero di telefono e la tua email";
// Service terms - Variant for identity server when displayed out of a context
"service_terms_modal_title_identity_server" = "Trova contatti";
"service_terms_modal_message_identity_server" = "Accetta i Termini di servizio dell'Identity Server (%@) per trovare altri contatti.";
// Generic errors
"error_invite_3pid_with_no_identity_server" = "Aggiungi un Identity Server nelle Impostazioni per poter invitare utenti tramite email.";

View file

@ -2,5 +2,5 @@
"NSCameraUsageDescription" = "카메라는 사진과 영상 촬영, 영상 통화를 하는 데 쓰입니다.";
"NSPhotoLibraryUsageDescription" = "포토 라이브러리는 사진과 영상을 보내는데 쓰입니다.";
"NSMicrophoneUsageDescription" = "마이크는 영상 촬영, 통화에 쓰입니다.";
"NSContactsUsageDescription" = "연락처에 있는 상대가 이미 Riot이나 Matrix를 이용하고 있다는 걸 보여주기 위해 연락처의 이메일 주소와 전화번호를 Matrix 아이덴티티 서버로 보낼 수 있습니다. 새 Vector는 이 자료를 저장하거나 다른 용도로 사용하지 않습니다. 자세한 내용은 애플리케이션 설정에 있는 개인정보 보호정책을 읽어주세요.";
"NSContactsUsageDescription" = "이미 Matrix를 사용하는 연락처 사람들을 검색하기 위해, Riot은 당신이 선택한 Matrix ID 서버로 이메일 주소와 전화번호를 보낼 수 있습니다. 서버가 지원된다면, 개인 정보는 보내기 전에 해시됩니다 - 더 자세한 정보는 ID 서버의 개인 정보 정책을 확인해주세요.";
"NSCalendarsUsageDescription" = "앱에서 예정된 회의를 봅니다.";

View file

@ -1,34 +1,56 @@
/* New message from a specific person, not referencing a room */
"MSG_FROM_USER" = "%@가 보낸 메시지";
"MSG_FROM_USER" = "%@님이 메시지를 보냈습니다";
/* New message from a specific person in a named room */
"MSG_FROM_USER_IN_ROOM" = "%@ 방에 게시 %@";
"MSG_FROM_USER_IN_ROOM" = "%@님, %@ 방에 게시함";
/* New message from a specific person, not referencing a room. Content included. */
"MSG_FROM_USER_WITH_CONTENT" = "%@: %@";
/* New message from a specific person in a named room. Content included. */
"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@: %@ 안의 %@";
"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@님, %@ 방에서: %@";
/* New action message from a specific person, not referencing a room. */
"ACTION_FROM_USER" = "* %@ %@";
/* New action message from a specific person in a named room. */
"ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@";
/* New action message from a specific person, not referencing a room. */
"IMAGE_FROM_USER" = "%@ 보낸 사진 %@";
"IMAGE_FROM_USER" = "%@님이 사진을 보냈습니다 %@";
/* A single unread message in a room */
"SINGLE_UNREAD_IN_ROOM" = "%@에서 메시지를 받았습니다";
"SINGLE_UNREAD_IN_ROOM" = "%@에서 메시지를 받았습니다";
/* A single unread message */
"SINGLE_UNREAD" = "메시지를 받았습니다";
/* A user has invited you to a chat */
"USER_INVITE_TO_CHAT" = "%@가 대화에 당신을 초대했습니다";
"USER_INVITE_TO_CHAT" = "%@님이 당신을 대화에 초대했습니다";
/* A user has invited you to an (unamed) group chat */
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ 그룹 대화에 당신을 초대했습니다";
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@님이 그룹 대화에 당신을 초대했습니다";
/* Incoming one-to-one voice call */
"VOICE_CALL_FROM_USER" = "%@이 건 통화";
"VOICE_CALL_FROM_USER" = "%@님이 건 전화";
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "%@가 건 영상통화";
"VIDEO_CALL_FROM_USER" = "%@님이 건 영상 통화";
/* Incoming unnamed voice conference invite from a specific person */
"VOICE_CONF_FROM_USER" = "%@이 건 그룹통화";
"VOICE_CONF_FROM_USER" = "%@이 건 그룹 통화";
/* Incoming unnamed video conference invite from a specific person */
"VIDEO_CONF_FROM_USER" = "%@이 건 영상그룹통화";
"VIDEO_CONF_FROM_USER" = "%@이 건 영상 그룹 통화";
/* Sticker from a specific person, not referencing a room. */
"STICKER_FROM_USER" = "%@ 스티커를 보냈습니다";
"STICKER_FROM_USER" = "%@님이 스티커를 보냈습니다";
/* A user has invited you to a named room */
"USER_INVITE_TO_NAMED_ROOM" = "%@가 %@로 당신을 초대했습니다";
"USER_INVITE_TO_NAMED_ROOM" = "%@님이 %@ 방으로 당신을 초대했습니다";
/* Message title for a specific person in a named room */
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@님, %@ 방에 있음";
/* New action message from a specific person in a named room. */
"IMAGE_FROM_USER_IN_ROOM" = "%@님이 사진을 보냈습니다 %@, %@ 방에서";
/* Multiple unread messages in a room */
"UNREAD_IN_ROOM" = "%@개의 새 메시지, %@ 방에서";
/* Multiple unread messages from a specific person, not referencing a room */
"MSGS_FROM_USER" = "%@개의 새 메시지, %@님으로부터";
/* Multiple unread messages from two people */
"MSGS_FROM_TWO_USERS" = "%@개의 새 메시지, %@님과 %@님으로부터";
/* Multiple unread messages from three people */
"MSGS_FROM_THREE_USERS" = "%@개의 새 메시지, %@님과 %@님, %@님으로부터";
/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */
"MSGS_FROM_TWO_PLUS_USERS" = "%@개의 새 메시지, %@님과 %@님 외 여러 명으로부터";
/* Multiple messages in two rooms */
"MSGS_IN_TWO_ROOMS" = "%@개의 새 메시지, %@ 방과 %@ 방에서";
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@개의 새 메시지, %@ 방과 %@ 방 외 여러 방에서";
/* Incoming named voice conference invite from a specific person */
"VOICE_CONF_NAMED_FROM_USER" = "%@님이 건 그룹 통화: '%@'";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "%@님이 건 영상 그룹 통화: '%@'";

View file

@ -13,12 +13,874 @@
"create" = "만들기";
"start" = "시작";
"leave" = "떠나기";
"remove" = "지우기";
"remove" = "감추기";
"invite" = "초대";
"retry" = "다시해보기";
"retry" = "다시 시도";
"on" = "켜기";
"off" = "끄기";
"cancel" = "취소";
"save" = "저장";
"preview" = "미리 보기";
"camera" = "카메라";
// String for App Store
"store_short_description" = "안전한 분산 대화/VoIP";
"store_full_description" = "여러분의 통제 하에 완전히 유연한 대화 앱. Riot은 여러분이 원하는 방식으로 대화할 수 있도록 합니다. 개방형 분산 커뮤니티의 표준 - [matrix]를 위해 제작됨. \n \n무료 matrix.org 계정을 만들고, https://modular.im에서 자신만의 서버, 혹은 다른 Matrix 서버를 얻으세요. \n \n왜 Riot.im을 선택해야 하나요? \n \n완전한 대화: 원하는 대로 팀이나 친구, 커뮤니티를 중심으로 방을 만드세요! 대화, 파일 공유, 위젯 추가와 음성 및 영상 통화 - 모두 무료입니다. \n \n강력한 통합: 여러분이 알고 사랑하는 도구와 함께 Riot.im을 사용하세요. Riot.im이라면 다른 대화 앱의 사용자와 그룹까지도 대화할 수 있습니다. \n \n개인 및 보안: 대화를 비밀로 유지하세요. 최첨단 종단간 암호화로 비밀 대화를 은밀하게 유지해줍니다. \n \n오픈 소스: Matrix로 만들어진 오픈 소스입니다. 자신의 데이터를 자신의 서버에 소유하거나, 신뢰하는 서버에 맡기세요. \n \n어디에 있든: 모든 기기나 https://riot.im에서 완전히 동기화된 메시지 기록으로 연락을 유지합니다.";
"join" = "참가";
"decline" = "끊기";
"accept" = "수락";
"voice" = "음성";
"video" = "영상";
"active_call" = "현재 전화";
"active_call_details" = "현재 전화 (%@)";
"later" = "나중에";
"rename" = "이름 다시 짓기";
"collapse" = "접기";
"send_to" = "%@님에게 보내기";
"sending" = "보내는 중";
"close" = "닫기";
// Authentication
"auth_login" = "로그인";
"auth_register" = "등록";
"auth_submit" = "제출";
"auth_skip" = "넘기기";
"auth_login_single_sign_on" = "통합 인증(SSO)으로 로그인";
"auth_send_reset_email" = "초기화 이메일 보내기";
"auth_return_to_login" = "로그인 화면으로 돌아가기";
"auth_user_id_placeholder" = "이메일 혹은 사용자 이름";
"auth_password_placeholder" = "비밀번호";
"auth_new_password_placeholder" = "새 비밀번호";
"auth_user_name_placeholder" = "사용자 이름";
"auth_optional_email_placeholder" = "이메일 주소 (선택)";
"auth_email_placeholder" = "이메일 주소";
"auth_optional_phone_placeholder" = "전화번호 (선택)";
"auth_phone_placeholder" = "전화번호";
"auth_repeat_password_placeholder" = "비밀번호 다시 입력";
"auth_repeat_new_password_placeholder" = "새 비밀번호 확인";
"auth_home_server_placeholder" = "URL (예: https://matrix.org)";
"auth_identity_server_placeholder" = "URL (예: https://vector.im)";
"auth_invalid_login_param" = "맞지 않은 사용자 이름 혹은 비밀번호";
"auth_invalid_user_name" = "사용자 이름에는 문자, 숫자, 점, 하이픈(-)과 밑줄(_)만 들어갑니다";
"auth_invalid_password" = "비밀번호가 너무 짧음 (최소 6자 이상)";
"auth_invalid_email" = "올바른 이메일 주소가 아닙니다";
"auth_invalid_phone" = "올바른 전화번호가 아닙니다";
"auth_missing_password" = "비밀번호가 누락되었습니다";
"auth_add_email_message" = "계정에 이메일 주소를 추가해 다른 사용자가 당신을 검색할 수 있고, 비밀번호를 다시 설정할 수 있습니다.";
"auth_add_phone_message" = "계정에 전화번호를 추가해 다른 사용자가 당신을 검색할 수 있습니다.";
"auth_add_email_phone_message" = "계정에 이메일 주소 그리고/혹은 전화번호를 추가해 다른 사용자가 당신을 검색할 수 있습니다. 이메일 주소는 비밀번호를 다시 설정하는 데에도 사용됩니다.";
"auth_add_email_and_phone_message" = "이메일 주소와 전화번호를 추가해 다른 사용자가 당신을 검색할 수 있습니다. 이메일 주소는 비밀번호를 다시 설정하는 데에도 사용됩니다.";
"auth_missing_email" = "이메일 주소가 누락되었습니다";
"auth_missing_phone" = "전화번호가 누락되었습니다";
"auth_missing_email_or_phone" = "이메일 주소 혹은 전화번호가 누락되었습니다";
"auth_email_in_use" = "이 이메일 주소는 이미 사용 중입니다";
"auth_phone_in_use" = "이 전화번호는 이미 사용 중입니다";
"auth_untrusted_id_server" = "ID 서버를 신뢰할 수 없습니다";
"auth_password_dont_match" = "비밀번호가 맞지 않음";
"auth_username_in_use" = "사용 중인 사용자 이름";
"auth_forgot_password" = "비밀번호를 잊었습니까?";
"auth_forgot_password_error_no_configured_identity_server" = "ID 서버를 설정할 수 없음: 비밀번호를 다시 설정하기 위해 하나를 추가하세요.";
"auth_email_not_found" = "이메일 전송에 실패함: 이 이메일 주소를 찾을 수 없음";
"auth_use_server_options" = "맞춤 서버 옵션 사용 (고급)";
"auth_email_validation_message" = "가입을 계속하려면 이메일을 확인해주세요";
"auth_msisdn_validation_title" = "확인 보류 중";
"auth_msisdn_validation_message" = "활성 코드가 있는 SMS를 보냈습니다. 아래에 이 코드를 입력해주세요.";
"auth_msisdn_validation_error" = "전화번호를 확인할 수 없습니다.";
"auth_recaptcha_message" = "이 홈서버는 당신이 로봇이 아닌지 알고 싶습니다";
"auth_reset_password_message" = "비밀번호를 다시 설정하려면, 계정에 연결된 이메일 주소를 입력하세요:";
"auth_reset_password_missing_email" = "계정에 연결된 이메일 주소를 입력해야 합니다.";
"auth_reset_password_missing_password" = "새 비밀번호를 입력해야 합니다.";
"auth_reset_password_email_validation_message" = "이메일을 %@(으)로 보냈습니다. 링크를 따라간 뒤, 아래를 클릭하세요.";
"auth_reset_password_next_step_button" = "저는 제 이메일 주소를 확인했습니다";
"auth_reset_password_error_unauthorized" = "이메일 주소 확인에 실패함: 이메일에 있는 링크를 클릭했는 지 확인하세요";
"auth_reset_password_error_not_found" = "이메일 주소가 이 홈서버의 Matrix ID에 연결되어 있지 않습니다.";
"auth_reset_password_success_message" = "비밀번호가 다시 설정되었습니다.\n\n모든 기기에서 로그아웃되며 더 이상 푸시 알림을 받을 수 없습니다. 알림을 다시 켜려면 각 기기마다 다시 로그인하세요.";
"auth_add_email_and_phone_warning" = "API가 있기 전까진 이메일과 전화번호를 동시에 등록하는 것을 지원하지 않습니다. 오직 계정에는 전화번호만 들어갑니다. 설정에서 프로필에 이메일을 추가할 수 있습니다.";
"auth_accept_policies" = "이 홈서버의 정책을 검토하고 수락해주세요:";
"auth_autodiscover_invalid_response" = "잘못된 홈서버 검색 응답";
"auth_softlogout_signed_out" = "로그아웃했습니다";
"auth_softlogout_sign_in" = "로그인";
"auth_softlogout_reason" = "당신의 홈서버 (%1$@) 관리자가 당신의 계정 %2$@을(를) 로그아웃했습니다 (%3$@).";
"auth_softlogout_recover_encryption_keys" = "이 기기에 독점적으로 저장된 암호화 키를 복구하려면 로그인하세요. 이 키가 있어야 기기에서 모든 보안 메시지를 읽을 수 있습니다.";
"auth_softlogout_clear_data" = "개인 정보 지우기";
"auth_softlogout_clear_data_message_1" = "경고: (암호화 키를 포함한) 개인 정보는 이 기기에 여전히 저장됩니다.";
"auth_softlogout_clear_data_message_2" = "이 기기를 그만 사용하거나 다른 계정으로 로그인하고 싶은 경우에 지우세요.";
"auth_softlogout_clear_data_button" = "모든 데이터 지우기";
"auth_softlogout_clear_data_sign_out_title" = "확신합니까?";
"auth_softlogout_clear_data_sign_out_msg" = "현재 이 기기에 저장된 모든 데이터를 지우겠습니까? 계정 데이터와 메시지에 접근하도록 다시 로그인하세요.";
"auth_softlogout_clear_data_sign_out" = "로그아웃";
// Chat creation
"room_creation_title" = "새 대화";
"room_creation_account" = "계정";
"room_creation_appearance" = "모습";
"room_creation_appearance_name" = "이름";
"room_creation_appearance_picture" = "대화 사진 (선택)";
"room_creation_privacy" = "개인";
"room_creation_private_room" = "이 대화는 개인입니다";
"room_creation_public_room" = "이 대화는 공개입니다";
"room_creation_make_public" = "공개로 하기";
"room_creation_make_public_prompt_title" = "이 대화를 공개로 합니까?";
"room_creation_make_public_prompt_msg" = "이 대화를 공개로 하겠습니까? 누구나 메시지를 읽고 대화에 참여할 수 있습니다.";
"room_creation_keep_private" = "개인으로 유지";
"room_creation_make_private" = "개인으로 하기";
"room_creation_wait_for_creation" = "방을 이미 만드는 중입니다. 기다려주세요.";
"room_creation_invite_another_user" = "검색 / 사용자 ID, 이름 또는 이메일로 초대";
"room_creation_error_invite_user_by_email_without_identity_server" = "ID 서버가 설정되지 않아 이메일로 참가자를 추가할 수 없습니다.";
// Room recents
"room_recents_directory_section" = "방 목록";
"room_recents_directory_section_network" = "네트워크";
"room_recents_favourites_section" = "즐겨찾기";
"room_recents_people_section" = "사람";
"room_recents_conversations_section" = "방";
"room_recents_no_conversation" = "방 없음";
"room_recents_low_priority_section" = "중요하지 않음";
"room_recents_server_notice_section" = "시스템 경고";
"room_recents_invites_section" = "초대";
"room_recents_start_chat_with" = "대화 시작";
"room_recents_create_empty_room" = "방 만들기";
"room_recents_join_room" = "방 참가";
"room_recents_join_room_title" = "방 참가";
"room_recents_join_room_prompt" = "방 ID 또는 방 별칭을 입력";
// People tab
"people_invites_section" = "초대";
"people_conversation_section" = "대화";
"people_no_conversation" = "대화 없음";
// Rooms tab
"room_directory_no_public_room" = "이용할 수 있는 공개 방 없음";
// Groups tab
"group_invite_section" = "초대";
"group_section" = "커뮤니티";
// Search
"search_rooms" = "방";
"search_messages" = "메시지";
"search_people" = "사람";
"search_files" = "파일";
"search_default_placeholder" = "검색";
"search_people_placeholder" = "사용자 ID, 이름 또는 이메일로 검색";
"search_no_result" = "결과 없음";
"search_in_progress" = "검색 중…";
// Directory
"directory_cell_title" = "목록 찾기";
"directory_cell_description" = "%tu개의 방";
"directory_search_results_title" = "목록 결과 찾기";
"directory_search_results" = "%@(으)로 찾은 %tu개의 결과";
"directory_search_results_more_than" = ">%@(으)로 찾은 %tu개의 결과";
"directory_searching_title" = "목록 검색 중…";
"directory_search_fail" = "데이터 가져오기에 실패함";
// Contacts
"contacts_address_book_section" = "로컬 연락처";
"contacts_address_book_matrix_users_toggle" = "Matrix 사용자만";
"contacts_address_book_no_contact" = "로컬 연락처 없음";
"contacts_address_book_permission_required" = "로컬 연락처에 접근하는데 필요한 권한";
"contacts_address_book_permission_denied" = "Riot이 로컬 연락처에 접근하도록 허용하지 않음";
"contacts_user_directory_section" = "사용자 목록";
"contacts_user_directory_offline_section" = "사용자 목록 (오프라인)";
// Chat participants
"room_participants_title" = "참가자";
"room_participants_add_participant" = "참가자 추가";
"room_participants_one_participant" = "1명의 참가자";
"room_participants_multi_participants" = "%d명의 참가자";
"room_participants_leave_prompt_title" = "방 떠나기";
"room_participants_leave_prompt_msg" = "방을 떠나겠습니까?";
"room_participants_remove_prompt_title" = "확인";
"room_participants_remove_prompt_msg" = "이 대화에서 %@을(를) 제거하겠습니까?";
"room_participants_remove_third_party_invite_msg" = "API가 있기 전까진 제 3자 초대를 제거하는 것은 아직 지원하지 않습니다";
"room_participants_invite_prompt_title" = "확인";
"room_participants_invite_prompt_msg" = "%@님을 이 대화에 초대하겠습니까?";
"room_participants_filter_room_members" = "방 구성원 필터";
"room_participants_invite_another_user" = "검색 / 사용자 ID, 이름 또는 이메일로 초대";
"room_participants_invite_malformed_id_title" = "초대 오류";
"room_participants_invite_malformed_id" = "잘못된 ID. 이메일 주소 혹은 '@localpart:domain'과 같은 Matrix ID이어야 합니다";
"room_participants_invited_section" = "초대받음";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "ID 서버가 설정되지 않아서 이메일을 사용하는 연락처와 대화를 시작할 수 없습니다.";
"room_participants_online" = "온라인";
"room_participants_offline" = "오프라인";
"room_participants_unknown" = "알 수 없음";
"room_participants_idle" = "대기 중";
"room_participants_now" = "지금";
"room_participants_ago" = "전";
"room_participants_action_section_admin_tools" = "관리자 도구";
"room_participants_action_section_direct_chats" = "다이렉트 대화";
"room_participants_action_section_devices" = "기기";
"room_participants_action_section_other" = "기타";
"room_participants_action_invite" = "초대";
"room_participants_action_leave" = "이 방 떠나기";
"room_participants_action_remove" = "이 방에서 제거";
"room_participants_action_ban" = "이 방에서 차단";
"room_participants_action_unban" = "차단 풀기";
"room_participants_action_ignore" = "이 사용자의 모든 메시지 숨기기";
"room_participants_action_unignore" = "이 사용자의 모든 메시지 보이기";
"room_participants_action_set_default_power_level" = "보통 사용자로 다시 설정";
"room_participants_action_set_moderator" = "중재자로 하기";
"room_participants_action_set_admin" = "관리자로 하기";
"room_participants_action_start_new_chat" = "새 대화 시작";
"room_participants_action_start_voice_call" = "음성 통화 시작";
"room_participants_action_start_video_call" = "영상 통화 시작";
"room_participants_action_mention" = "언급";
// Chat
"room_jump_to_first_unread" = "처음으로 읽지 않은 메시지로 가기";
"room_new_message_notification" = "%d개의 새 메시지";
"room_new_messages_notification" = "%d개의 새 메시지";
"room_one_user_is_typing" = "%@님이 입력하는 중…";
"room_two_users_are_typing" = "%@님 & %@님이 입력하는 중…";
"room_many_users_are_typing" = "%@님, %@님 외 여러 명이 입력하는 중…";
"room_message_placeholder" = "(암호화 안 된) 메시지를 보내세요…";
"room_message_reply_to_placeholder" = "(암호화 안 된) 답장을 보내세요…";
"room_message_unable_open_link_error_message" = "링크를 열 수 없습니다.";
"room_do_not_have_permission_to_post" = "이 방에 글을 올릴 권한이 없습니다";
"encrypted_room_message_placeholder" = "암호화된 메시지를 보내세요…";
"encrypted_room_message_reply_to_placeholder" = "암호화된 답장을 보내세요…";
"room_message_short_placeholder" = "메시지를 보내세요…";
"room_message_reply_to_short_placeholder" = "답장을 보내세요…";
"room_offline_notification" = "서버와의 연결이 끊겼습니다.";
"room_unsent_messages_notification" = "메시지가 보내지지 않았습니다. %1$s, %2$s, 둘 중 어느 것을 하겠습니까?";
"room_unsent_messages_unknown_devices_notification" = "알 수 없는 기기가 있어 메시지가 보내지지 않았습니다. %1$s, %2$s, 둘 중 어느 것을 하겠습니까?";
"room_ongoing_conference_call" = "회의 전화가 진행 중입니다. %1$s 또는 %2$s로 참가하세요.";
"room_ongoing_conference_call_with_close" = "회의 전화가 진행 중입니다. %1$s 또는 %2$s로 참가하세요. %@.";
"room_ongoing_conference_call_close" = "닫기";
"room_conference_call_no_power" = "이 방에서 회의 전화를 관리할 권한이 필요합니다";
"room_prompt_resend" = "모두 다시 보내기";
"room_prompt_cancel" = "모두 취소하기";
"room_resend_unsent_messages" = "보내지지 않은 메시지 다시 보내기";
"room_delete_unsent_messages" = "보내지지 않은 메시지 삭제하기";
"room_event_action_copy" = "복사";
"room_event_action_quote" = "인용";
"room_event_action_redact" = "감추기";
"room_event_action_more" = "더 보기";
"room_event_action_share" = "공유";
"room_event_action_permalink" = "고유 링크";
"room_event_action_view_source" = "소스 보기";
"room_event_action_view_decrypted_source" = "암호화된 소스 보기";
"room_event_action_report" = "내용 보고하기";
"room_event_action_report_prompt_reason" = "이 내용을 보고하는 이유";
"room_event_action_kick_prompt_reason" = "이 사용자를 추방하는 이유";
"room_event_action_ban_prompt_reason" = "이 사용자를 차단하는 이유";
"room_event_action_report_prompt_ignore_user" = "이 사용자의 모든 메시지를 숨기겠습니까?";
"room_event_action_save" = "저장";
"room_event_action_resend" = "다시 보내기";
"room_event_action_delete" = "삭제";
"room_event_action_cancel_send" = "보내기 취소";
"room_event_action_cancel_download" = "다운로드 취소";
"room_event_action_view_encryption" = "암호화 정보";
"room_event_action_reply" = "답장";
"room_event_action_edit" = "편집";
"room_event_action_reaction_show_all" = "모두 보이기";
"room_event_action_reaction_show_less" = "적게 보이기";
"room_event_action_reaction_history" = "리액션 기록";
"room_warning_about_encryption" = "종단간 암호화는 베타 버전이고 신뢰하지 못할 수 있습니다.\n\n아직 데이터를 보호한다고 신뢰하지 마세요.\n\n기기가 방에 참가하기 전에 아직 기록을 해독할 수 없습니다.\n\n아직 암호화를 구현하지 않았기 때문에 암호화된 메시지는 클라이언트에 나타나지 않습니다.";
"room_event_failed_to_send" = "보내기에 실패함";
"room_action_camera" = "사진 또는 영상 찍기";
"room_action_send_photo_or_video" = "사진 또는 영상 보내기";
"room_action_send_sticker" = "스티커 보내기";
"room_action_send_file" = "파일 보내기";
"room_action_reply" = "답장";
"room_replacement_information" = "이 방은 대체되었으며 더 이상 활동하지 않습니다.";
"room_replacement_link" = "대화는 여기서 이어집니다.";
"room_predecessor_information" = "이 방에서 다른 대화가 이어집니다.";
"room_predecessor_link" = "여기를 눌러 과거 메시지를 보세요.";
"room_resource_limit_exceeded_message_contact_1" = " 제발 ";
"room_resource_limit_exceeded_message_contact_2_link" = "서비스 관리자와 연락해서";
"room_resource_limit_exceeded_message_contact_3" = " 이 서비스를 계속 사용하세요.";
"room_resource_usage_limit_reached_message_1_default" = "이 홈서버가 리소스 한도를 초과했습니다 따라서 ";
"room_resource_usage_limit_reached_message_1_monthly_active_user" = "이 홈서버가 월 간 활성 사용자 한도를 초과했습니다 따라서 ";
"room_resource_usage_limit_reached_message_2" = "일부 사용자는 로그인할 수 없습니다.";
"room_resource_usage_limit_reached_message_contact_3" = " 한도를 높이세요.";
"room_message_edits_history_title" = "메시지 편집";
// Unknown devices
"unknown_devices_alert_title" = "방에 알 수 없는 기기가 있음";
"unknown_devices_alert" = "이 방에 확인되지 않은 알 수 없는 기기가 있습니다.이 기기가 확인한 사용자의 것이라는 보장이 없습니다.\n계속하기 전에 각 기기마다 확인 절차를 거치기를 권합니다, 하지만 원한다면 확인하지 않고 메시지를 다시 보낼 수 있습니다.";
"unknown_devices_send_anyway" = "무시하고 보내기";
"unknown_devices_call_anyway" = "무시하고 전화";
"unknown_devices_answer_anyway" = "무시하고 답하기";
"unknown_devices_verify" = "확인하기…";
"unknown_devices_title" = "알 수 없는 기기";
// Room Title
"room_title_new_room" = "새 방";
"room_title_multiple_active_members" = "%@/%@ 활동 중인 구성원";
"room_title_one_active_member" = "%@/%@ 활동 중인 구성원";
"room_title_invite_members" = "구성원 초대";
"room_title_members" = "%@명의 구성원";
"room_title_one_member" = "1명의 구성원";
// Room Preview
"room_preview_invitation_format" = "%@님에게 이 방을 초대받았습니다";
"room_preview_subtitle" = "이 방의 미리 보기입니다. 방 상호작용은 꺼져 있습니다.";
"room_preview_unlinked_email_warning" = "이 초대는 %@님이 보냈습니다, 이 계정과는 연관이 없습니다. 다른 계정으로 로그인하거나, 이 이메일을 계정에 추가하세요.";
"room_preview_try_join_an_unknown_room" = "%@(으)로 접근하려 했습니다. 토론에 참여하기 위해 참가하는 건가요?";
"room_preview_try_join_an_unknown_room_default" = "방";
// Settings
"settings_title" = "설정";
"account_logout_all" = "모든 계정에서 로그아웃";
"settings_config_no_build_info" = "빌드 정보 없음";
"settings_mark_all_as_read" = "모든 메시지를 읽음으로 표시";
"settings_report_bug" = "버그 신고";
"settings_clear_cache" = "캐시 지우기";
"settings_config_home_server" = "홈서버: %@";
"settings_config_identity_server" = "ID 서버: %@";
"settings_config_user_id" = "사용자 ID: %@";
"settings_user_settings" = "사용자 설정";
"settings_notifications_settings" = "알림 설정";
"settings_calls_settings" = "전화";
"settings_user_interface" = "사용자 인터페이스";
"settings_ignored_users" = "무시한 사용자";
"settings_contacts" = "로컬 연락처";
"settings_advanced" = "고급";
"settings_other" = "기타";
"settings_labs" = "연구실";
"settings_flair" = "허용되는 곳에 재능 보이기";
"settings_devices" = "기기";
"settings_cryptography" = "암호화";
"settings_key_backup" = "키 백업";
"settings_deactivate_account" = "계정 비활성화";
"settings_sign_out" = "로그아웃";
"settings_sign_out_confirmation" = "확신합니까?";
"settings_sign_out_e2e_warn" = "종단간 암호화 키를 잃게 됩니다. 더 이상 이 기기로 암호화된 방의 예전 메시지를 읽을 수 없게 됩니다.";
"settings_profile_picture" = "프로필 사진";
"settings_display_name" = "표시 이름";
"settings_first_name" = "성";
"settings_surname" = "이름";
"settings_remove_prompt_title" = "확인";
"settings_remove_email_prompt_msg" = "이메일 주소 %@을(를) 제거하겠습니까?";
"settings_remove_phone_prompt_msg" = "전화번호 %@을(를) 지우겠습니까?";
"settings_email_address" = "이메일";
"settings_email_address_placeholder" = "이메일 주소를 입력하세요";
"settings_add_email_address" = "이메일 주소 추가";
"settings_phone_number" = "휴대 전화";
"settings_add_phone_number" = "전화번호 추가";
"settings_change_password" = "비밀번호 변경";
"settings_night_mode" = "야간 모드";
"settings_fail_to_update_profile" = "프로필 업데이트에 실패함";
"settings_enable_push_notif" = "이 기기에서 알림";
"settings_show_decrypted_content" = "암호화된 내용 보이기";
"settings_global_settings_info" = "당신의 %@ 웹 클라이언트에 전역 알림 설정이 켜졌습니다";
"settings_pin_rooms_with_missed_notif" = "알림을 놓친 방을 고정";
"settings_pin_rooms_with_unread" = "읽지 않은 메시지가 있는 방을 고정";
"settings_on_denied_notification" = "%@에 의해 알림이 거부됬습니다, 기기 설정에서 %@을(를) 허용해주세요";
"settings_enable_callkit" = "통합 전화";
"settings_callkit_info" = "잠금 화면에서 오는 전화를 받습니다. 시스템의 통화 내역에서 Riot 통화를 확인하세요. iCloud가 켜져있다면, 이 통화 기록은 Apple과 공유됩니다.";
"settings_ui_language" = "언어";
"settings_ui_theme" = "테마";
"settings_ui_theme_auto" = "자동";
"settings_ui_theme_light" = "밝게";
"settings_ui_theme_dark" = "어둡게";
"settings_ui_theme_black" = "검정";
"settings_ui_theme_picker_title" = "테마를 선택하세요";
"settings_ui_theme_picker_message" = "\"자동\"은 기기의 \"색상 반전\" 설정을 사용합니다";
"settings_unignore_user" = "%@님의 모든 메시지를 표시합니까?";
"settings_contacts_discover_matrix_users" = "이메일과 전화번호로 사용자를 검색";
"settings_contacts_phonebook_country" = "국제전화 나라 번호";
"settings_labs_e2e_encryption" = "종단간 암호화";
"settings_labs_e2e_encryption_prompt_message" = "암호화 설정을 마치려면 다시 로그인하세요.";
"settings_labs_room_members_lazy_loading" = "방 구성원 불러오기 지연";
"settings_labs_room_members_lazy_loading_error_message" = "홈서버가 아직 방 구성원 불러오기 지연을 지원하지 않습니다. 나중에 다시 시도해보세요.";
"settings_labs_create_conference_with_jitsi" = "Jitsi로 회의 전화 만들기";
"settings_labs_message_reaction" = "메시지에 이모지로 리액션하기";
"settings_version" = "버전 %@";
"settings_olm_version" = "Olm 버전 %@";
"settings_copyright" = "저작권";
"settings_copyright_url" = "https://riot.im/copyright";
"settings_term_conditions" = "이용 약관";
"settings_term_conditions_url" = "https://riot.im/tac_apple";
"settings_privacy_policy" = "개인 정보 정책";
"settings_privacy_policy_url" = "https://riot.im/privacy";
"settings_third_party_notices" = "제 3자 공지";
"settings_send_crash_report" = "충돌 & 사용 데이터 보내기";
"settings_enable_rageshake" = "분노의 흔들기로 버그 신고";
"settings_old_password" = "이전 비밀번호";
"settings_new_password" = "새 비밀번호";
"settings_confirm_password" = "비밀번호 확인";
"settings_fail_to_update_password" = "비밀번호 업데이트에 실패함";
"settings_password_updated" = "비밀번호가 업데이트되었습니다";
"settings_crypto_device_name" = "기기 공개 이름: ";
"settings_crypto_device_id" = "\n기기 ID: ";
"settings_crypto_device_key" = "\n기기 키:\n";
"settings_crypto_export" = "키 내보내기";
"settings_crypto_blacklist_unverified_devices" = "확인된 기기만 암호화";
"settings_deactivate_my_account" = "내 계정 비활성화";
"settings_key_backup_info" = "암호화된 메시지는 종단간 암호화로 보호됩니다. 오직 당신과 참가자만 키를 갖고 있어서 이 메시지를 읽을 수 있습니다.";
"settings_key_backup_info_checking" = "확인 중...";
"settings_key_backup_info_none" = "키가 이 기기에서 백업되지 않았습니다.";
"settings_key_backup_info_signout_warning" = "이 기기에만 있을 수 있는 키를 잃지 않도록, 로그아웃하기 전에 이 기기를 키 백업에 연결하세요.";
"settings_key_backup_info_version" = "키 백업 버전: %@";
"settings_key_backup_info_algorithm" = "알고리즘: %@";
"settings_key_backup_info_valid" = "이 기기는 키를 백업하고 있습니다.";
"settings_key_backup_info_not_valid" = "이 기기는 키를 백업하고 있지 않습니다, 하지만 존재하는 백업이 있고 이것을 복구한 뒤 키를 추가할 수 있습니다.";
"settings_key_backup_info_progress" = "%@ 키를 백업하는 중...";
"settings_key_backup_info_progress_done" = "모든 키가 백업됨";
"settings_key_backup_info_trust_signature_unknown" = "백업은 기기로부터 ID로 된 서명을 갖습니다: %@";
"settings_key_backup_info_trust_signature_valid" = "백업이 이 기기로부터 올바른 서명을 갖고 있음";
"settings_key_backup_info_trust_signature_valid_device_verified" = "백업이 %@(으)로부터 올바른 서명을 갖고 있음";
"settings_key_backup_info_trust_signature_valid_device_unverified" = "백업이 %@(으)로부터 서명을 갖고 있음";
"settings_key_backup_info_trust_signature_invalid_device_verified" = "백업이 %@(으)로부터 올바르지 않은 서명을 갖고 있음";
"settings_key_backup_info_trust_signature_invalid_device_unverified" = "백업이 %@(으)로부터 올바르지 않은 서명을 갖고 있음";
"settings_key_backup_button_create" = "키 백업 시작";
"settings_key_backup_button_restore" = "백업에서 복구";
"settings_key_backup_button_delete" = "백업 삭제";
"settings_key_backup_button_connect" = "이 기기를 키 백업에 연결";
"settings_key_backup_delete_confirmation_prompt_title" = "백업 삭제";
"settings_key_backup_delete_confirmation_prompt_msg" = "확신합니까? 키가 정상적으로 백업되지 않으면 암호화된 메시지를 잃게 됩니다.";
// Room Details
"room_details_title" = "방 세부 사항";
"room_details_people" = "구성원";
"room_details_files" = "파일";
"room_details_settings" = "설정";
"room_details_photo" = "방 사진";
"room_details_room_name" = "방 이름";
"room_details_topic" = "주제";
"room_details_favourite_tag" = "즐겨찾기";
"room_details_low_priority_tag" = "중요하지 않음";
"room_details_mute_notifs" = "알림 음소거";
"room_details_direct_chat" = "다이렉트 대화";
"room_details_access_section" = "이 방에 누가 접근할 수 있나요?";
"room_details_access_section_invited_only" = "초대한 사람들만";
"room_details_access_section_anyone_apart_from_guest" = "방의 링크를 아는 누구나, 손님 제외";
"room_details_access_section_anyone" = "방의 링크를 아는 누구나, 손님 포함";
"room_details_access_section_no_address_warning" = "방으로 링크를 주려면, 주소가 필요합니다";
"room_details_access_section_directory_toggle" = "이 방을 방 목록에 배치";
"room_details_history_section" = "누가 기록을 읽을 수 있나요?";
"room_details_history_section_anyone" = "누구나";
"room_details_history_section_members_only" = "(이 옵션을 선택한 시점부터) 구성원만";
"room_details_history_section_members_only_since_invited" = "(초대받은 시점부터) 구성원만";
"room_details_history_section_members_only_since_joined" = "(참가한 시점부터) 구성원만";
"room_details_history_section_prompt_title" = "개인 정보 경고";
"room_details_history_section_prompt_msg" = "이 방에서 기록을 읽을 수 있는 사람의 변경 사항은 이후 메시지부터 적용됩니다. 존재하는 기록의 가시성은 변하지 않습니다.";
"room_details_addresses_section" = "주소";
"room_details_no_local_addresses" = "이 방은 로컬 주소가 없습니다";
"room_details_new_address" = "새 주소 추가";
"room_details_new_address_placeholder" = "새 주소 추가 (예: #foo%@)";
"room_details_addresses_invalid_address_prompt_title" = "잘못된 별칭 형식";
"room_details_addresses_invalid_address_prompt_msg" = "%@은(는) 별칭에 올바른 형식이 아닙니다";
"room_details_addresses_disable_main_address_prompt_title" = "기본 주소 경고";
"room_details_addresses_disable_main_address_prompt_msg" = "지정한 기본 주소가 없습니다. 이 방의 기본 주소는 무작위로 지정됩니다";
"room_details_flair_section" = "커뮤니티 별 재능 보이기";
"room_details_new_flair_placeholder" = "새 커뮤니티 ID 추가 (예: +foo%@)";
"room_details_flair_invalid_id_prompt_title" = "잘못된 형식";
"room_details_flair_invalid_id_prompt_msg" = "%@은(는) 커뮤니티의 올바른 ID가 아닙니다";
"room_details_banned_users_section" = "차단한 사용자";
"room_details_advanced_section" = "고급";
"room_details_advanced_room_id" = "방 ID:";
"room_details_advanced_enable_e2e_encryption" = "암호화 켜짐 (경고: 다시 끌 수 없습니다!)";
"room_details_advanced_e2e_encryption_enabled" = "이 방의 암호화가 켜졌습니다";
"room_details_advanced_e2e_encryption_disabled" = "이 방의 암호화가 꺼졌습니다.";
"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "확인된 기기만 암호화";
"room_details_fail_to_update_avatar" = "방 사진 업데이트에 실패함";
"room_details_fail_to_update_room_name" = "방 이름 업데이트에 실패함";
"room_details_fail_to_update_topic" = "주제 업데이트에 실패함";
"room_details_fail_to_update_room_guest_access" = "방 손님 접근 업데이트에 실패함";
"room_details_fail_to_update_room_join_rule" = "참가 규칙 업데이트에 실패함";
"room_details_fail_to_update_room_directory_visibility" = "방 목록 가시성 업데이트에 실패함";
"room_details_fail_to_update_history_visibility" = "기록 가시성 업데이트에 실패함";
"room_details_fail_to_add_room_aliases" = "새 방 주소 추가에 실패함";
"room_details_fail_to_remove_room_aliases" = "방 주소 삭제에 실패함";
"room_details_fail_to_update_room_canonical_alias" = "기본 주소 업데이트에 실패함";
"room_details_fail_to_update_room_communities" = "관련된 커뮤니티 업데이트에 실패함";
"room_details_fail_to_update_room_direct" = "이 방의 다이렉트 플래그 업데이트에 실패함";
"room_details_fail_to_enable_encryption" = "이 방의 암호화 켜기에 실패함";
"room_details_save_changes_prompt" = "변경 사항을 저장할까요?";
"room_details_set_main_address" = "기본 주소로 설정";
"room_details_unset_main_address" = "기본 주소로 설정 해제";
"room_details_copy_room_id" = "방 ID 복사";
"room_details_copy_room_address" = "방 주소 복사";
"room_details_copy_room_url" = "방 URL 복사";
// Group Details
"group_details_title" = "커뮤니티 세부 사항";
"group_details_home" = "홈";
"group_details_people" = "사람";
"group_details_rooms" = "방";
// Group Home
"group_home_one_member_format" = "1멍의 구성원";
"group_home_multi_members_format" = "%tu명의 구성원";
"group_home_one_room_format" = "1개의 방";
"group_home_multi_rooms_format" = "%tu개의 방";
"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_invite_prompt_title" = "확인";
"group_participants_invite_prompt_msg" = "이 그룹에 %@님을 초대하겠습니까?";
"group_participants_filter_members" = "커뮤니티 구성원 필터";
"group_participants_invite_another_user" = "사용자 ID 또는 이름으로 검색 / 초대";
"group_participants_invite_malformed_id_title" = "초대 오류";
"group_participants_invite_malformed_id" = "잘못된 ID. '@localpart:domain'과 같은 Matrix ID이어야 합니다";
"group_participants_invited_section" = "초대받음";
// Group rooms
"group_rooms_filter_rooms" = "커뮤니티 방 필터";
// Read Receipts
"read_receipts_list" = "읽은 기록 목록";
"receipt_status_read" = "읽음: ";
// Media picker
"media_picker_title" = "미디어 라이브러리";
"media_picker_library" = "라이브러리";
"media_picker_select" = "선택";
// Image picker
"image_picker_action_camera" = "사진 찍기";
"image_picker_action_library" = "라이브러리에서 선택";
// Directory
"directory_title" = "목록";
"directory_server_picker_title" = "목록을 선택";
"directory_server_all_rooms" = "%@ 서버에 있는 모든 방";
"directory_server_all_native_rooms" = "모든 네이티브 Matrix 방";
"directory_server_type_homeserver" = "홈서버를 입력하면 공개 방 목록을 나열합니다";
"directory_server_placeholder" = "matrix.org";
// Events formatter
"event_formatter_member_updates" = "%tu명의 구성원 변경 사항";
"event_formatter_widget_added" = "%@ 위젯을 %@님이 추가했습니다";
"event_formatter_widget_removed" = "%@ 위젯을 %@님이 제거했습니다";
"event_formatter_jitsi_widget_added" = "%@님이 VoIP 회의를 추가했습니다";
"event_formatter_jitsi_widget_removed" = "%@님이 VoIP 회의를 제거했습니다";
"event_formatter_rerequest_keys_part1_link" = "암호화 키 다시 요청";
"event_formatter_rerequest_keys_part2" = " 다른 기기에서.";
"event_formatter_message_edited_mention" = "(편집됨)";
// Others
"or" = "또는";
"you" = "당신";
"today" = "오늘";
"yesterday" = "어제";
"network_offline_prompt" = "인터넷이 오프라인입니다.";
"homeserver_connection_lost" = "홈 서버에 연결할 수 없습니다.";
"public_room_section_title" = "공개 방 (%@에 있음):";
"bug_report_prompt" = "애플리케이션이 방금 충돌했군요.\n충돌 보고서를 제출하겠습니까?";
"rage_shake_prompt" = "좌절감에 휴대 전화를 흔들고 있군요. 버그 보고서를 제출하겠습니까?";
"do_not_ask_again" = "다시 묻지 않기";
"camera_access_not_granted" = "%@님은 카메라를 사용할 권한이 없습니다, 개인 정보 설정을 바꿔주세요";
"camera_unavailable" = "카메라는 이 기기에서 이용할 수 없습니다";
"photo_library_access_not_granted" = "%@님은 사진 라이브러리에 접근할 권한이 없습니다, 개인 정보 설정을 바꿔주세요";
"large_badge_value_k_format" = "%.1fK";
"room_does_not_exist" = "%@님은 없습니다";
// Call
"call_incoming_voice_prompt" = "%@님으로부터 온 음성 통화";
"call_incoming_video_prompt" = "%@님으로부터 온 영상 통화";
"call_incoming_voice" = "전화 오는 중...";
"call_incoming_video" = "오는 영상 통화...";
"call_already_displayed" = "이미 전화가 진행 중입니다.";
"call_jitsi_error" = "회의 전화 참가에 실패했습니다.";
// No VoIP support
"no_voip_title" = "전화 오는 중";
"no_voip" = "%@님이 당신에게 전화를 걸고 있지만 %@은(는) 아직 전화를 지원하지 않습니다.\n이 알림을 무시하고 다른 기기에서 전화를 받거나 전화를 거절할 수 있습니다.";
// Crash report
"google_analytics_use_prompt" = "%@이(가) 개선하도록 자동으로 익명의 충돌 보고서와 사용 데이터를 제공하겠습니까?";
// Crypto
"e2e_enabling_on_app_update" = "Riot는 이제 종단간 암호화를 지원하지만 암호화를 켜려면 다시 로그인해야 합니다.\n\n지금 다시 로그인하거나 나중에 애플리케이션 설정에서 할 수 있습니다.";
"e2e_need_log_in_again" = "이 기기에 종단간 암호화 키를 생성하고 공개 키를 홈서버에 제출하려면 다시 로그인해야 함니다.\n한 번만 하면 됩니다, 불편을 드려 죄송합니다.";
// Key backup wrong version
"e2e_key_backup_wrong_version_title" = "새 키 백업";
"e2e_key_backup_wrong_version" = "새 보안 메시지 키 백업이 감지되었습니다.\n\n당신이 한 것이 아니라면 설정에서 새 암호를 설정하세요.";
"e2e_key_backup_wrong_version_button_settings" = "설정";
"e2e_key_backup_wrong_version_button_wasme" = "접니다";
// Bug report
"bug_report_title" = "버그 신고";
"bug_report_description" = "버그를 설명해주세요. 무엇을 했나요? 무엇을 생각하고 했나요? 실제로 어떤 일이 일어났나요?";
"bug_crash_report_title" = "충돌 보고서";
"bug_crash_report_description" = "충돌 이전에 어떤 일을 했는 지 설명해주세요:";
"bug_report_logs_description" = "문제를 진단하기 위해, 이 클라이언트의 로그를 이 버그 보고서에 제출합니다. 위의 문자만 보내지길 원한다면, 선택을 해제해주세요:";
"bug_report_send_logs" = "로그 보내기";
"bug_report_send_screenshot" = "스크린샷 보내기";
"bug_report_progress_zipping" = "로그 수집 중";
"bug_report_progress_uploading" = "신고 업로드 중";
"bug_report_send" = "보내기";
// Widget
"widget_no_integrations_server_configured" = "설정된 통합 서버가 없음";
"widget_integrations_server_failed_to_connect" = "통합 서버 연결에 실패함";
"widget_no_power_to_manage" = "이 방에서 위젯을 관리할 권한이 없습니다";
"widget_creation_failure" = "위젯 생성에 실패함";
"widget_sticker_picker_no_stickerpacks_alert" = "현재 활성화된 스티커팩이 없습니다.";
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "지금 추가할까요?";
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "그렇게 하려면 사용자를 초대할 수 있어야 합니다.";
"widget_integration_unable_to_create" = "위젯 생성에 실패했습니다.";
"widget_integration_failed_to_send_request" = "요청 보내기에 실패했습니다.";
"widget_integration_room_not_recognised" = "이 방은 인식하지 않습니다.";
"widget_integration_positive_power_level" = "권한 등급은 양의 정수이어야 합니다.";
"widget_integration_must_be_in_room" = "당신은 이 방에 없습니다.";
"widget_integration_no_permission_in_room" = "이 방에 그런 작업을 할 권한이 없습니다.";
"widget_integration_missing_room_id" = "요청에 room_id가 없습니다.";
"widget_integration_missing_user_id" = "요청에 user_id가 없습니다.";
"widget_integration_room_not_visible" = "방 %@이(가) 보이지 않습니다.";
// Share extension
"share_extension_auth_prompt" = "내용을 공유하려면 기본 앱에 로그인";
"share_extension_failed_to_encrypt" = "보내기에 실패함. 기본 앱에서 이 방의 암호화 설정을 확인하세요";
// 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_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_review_now_action" = "지금 검토";
// Service terms
"service_terms_modal_title" = "서비스 약관";
"service_terms_modal_message" = "계속하려면 이 서비스 약관 (%@)에 동의해야 합니다.";
"service_terms_modal_accept_button" = "수락";
"service_terms_modal_description_for_identity_server" = "다른 사용자가 검색할 수 있음";
"service_terms_modal_description_for_integration_manager" = "봇, 브릿지, 위젯과 스티커팩을 사용";
"deactivate_account_title" = "계정 비활성화";
"deactivate_account_informations_part1" = "이것으로 계정은 영구적으로 사용할 수 없게됩니다. 로그인할 수 없고 아무도 같은 사용자 ID로 로그인할 수 없게 돱니다. 참가한 모든 방에서 떠나게되고 계정 세부 사항도 ID 서버에서 지워집니다. ";
"deactivate_account_informations_part2_emphasize" = "이 행동은 돌이킬 수 없습니다.";
"deactivate_account_informations_part3" = "\n\n계정 비활성화하기 ";
"deactivate_account_informations_part4_emphasize" = "우리는 보낸 메시지는 기본적으로 지우지 않습니다. ";
"deactivate_account_informations_part5" = "메시지를 지우고 싶다면 아래 상자를 체크하시요\n\nMatrix에서 메시지 가시성은 이메일과 유사합니다. 메시지를 지운다는 것은 보낸 메시지를 더 이상 새로운 혹은 등록하지 않은 사용자와 공유하는 않는 다는 것입니다, 하지만 이미 이 메시지에 접근한 등록한 사용자라면 그 사본에 계속 접근할 수 있습니다.";
"deactivate_account_forget_messages_information_part1" = "계정을 비활성화하면 보낸 모든 메시지를 지워주세요 (";
"deactivate_account_forget_messages_information_part2_emphasize" = "경고";
"deactivate_account_forget_messages_information_part3" = ": 이것은 이후 사용자가 불완전한 대화를 보게 됩니다)";
"deactivate_account_validate_action" = "계정 비활성화";
"deactivate_account_password_alert_title" = "계정 비활성화";
"deactivate_account_password_alert_message" = "계속하려면, 비밀번호를 입력해주세요";
// Re-request confirmation dialog
"rerequest_keys_alert_title" = "요청을 보냈습니다";
"rerequest_keys_alert_message" = "메시지를 해독해서 이 기기로 키를 보낼 수 있도록 Riot을 다른 기기에 설치해주세요.";
"key_backup_setup_title" = "키 백업";
"key_backup_setup_skip_alert_title" = "확신합니까?";
"key_backup_setup_skip_alert_message" = "로그아웃하거나 기기를 잃어버리면 보안 메시지를 잃게 됩니다.";
"key_backup_setup_skip_alert_skip_action" = "넘기기";
"key_backup_setup_intro_title" = "절대 암호화된 메시지를 잃지 마세요";
"key_backup_setup_intro_info" = "암호화된 방의 메시지는 종단간 암호화로 보호됩니다. 오직 당싱과 참가자만 키를 갖고 있어 메시지를 읽을 수 있습니다.\n\n키를 잃지 않도록 안전하게 백업하세요.";
"key_backup_setup_intro_setup_action_without_existing_backup" = "키 백업 시작";
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "이 기기를 키 백업에 연결";
"key_backup_setup_intro_manual_export_info" = "(고급)";
"key_backup_setup_intro_manual_export_action" = "수동으로 키 내보내기";
"key_backup_setup_passphrase_title" = "백업을 암호로 보호하기";
"key_backup_setup_passphrase_info" = "암호화된 키의 사본을 서버에 보관합니다. 암호로된 백업을 보호하며 안전하게 유지해줍니다.\n\n보안을 최대화하려면, 암호는 계정 비밀번호와 달라야 합니다.";
"key_backup_setup_passphrase_passphrase_title" = "입력";
"key_backup_setup_passphrase_passphrase_placeholder" = "암호 입력";
"key_backup_setup_passphrase_passphrase_valid" = "좋아요!";
"key_backup_setup_passphrase_passphrase_invalid" = "단어를 추가해보세요";
"key_backup_setup_passphrase_confirm_passphrase_title" = "확인";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "암호 확인";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "좋아요!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "암호가 맞지 않습니다";
"key_backup_setup_passphrase_set_passphrase_action" = "암호 설정";
"key_backup_setup_passphrase_setup_recovery_key_info" = "또는, 안전한 곳에 저장해 둘 복구 키로 백업을 보호합니다.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(고급) 복구 키로 설정";
"key_backup_setup_success_title" = "성공!";
// Success from passphrase
"key_backup_setup_success_from_passphrase_info" = "키가 백업되었습니다.\n\n복구 키는 안전망입니다 - 이것으로 암호를 잊어버려도 암호화된 메시지에 접근할 수 있습니다.\n\n복구키를 비밀번호 관리자 (혹은 금고)같은 안전한 장소에 두세요.";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "복구 키 저장";
"key_backup_setup_success_from_passphrase_done_action" = "끝";
// Success from recovery key
"key_backup_setup_success_from_recovery_key_info" = "키가 백업되었습니다.\n\n이 복구 키의 사본을 만들어 안전하게 보관하세요.";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "복구 키";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "사본 만들기";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "사본을 만들었습니다";
"key_backup_recover_title" = "보안 메시지";
"key_backup_recover_invalid_passphrase_title" = "맞지 않는 복구 암호";
"key_backup_recover_invalid_passphrase" = "이 암호로 백업을 해독할 수 없습니다: 올바른 복구 암호를 입력해서 확인해주세요.";
"key_backup_recover_invalid_recovery_key_title" = "복구 키가 맞지 않음";
"key_backup_recover_invalid_recovery_key" = "이 키로 백업을 해독할 수 없습니다: 올바른 복구 키를 입력해서 확인해주세요.";
"key_backup_recover_from_passphrase_info" = "복구 암호를 사용해 보안 메시지 기록을 푸세요";
"key_backup_recover_from_passphrase_passphrase_title" = "입력";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "암호 입력";
"key_backup_recover_from_passphrase_recover_action" = "기록 풀기";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "복구 암호가 기억나지 않나요? ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "복구 키를 사용하세요";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
"key_backup_recover_from_recovery_key_info" = "복구 키를 사용해 보안 메시지 기록을 풉니다";
"key_backup_recover_from_recovery_key_recovery_key_title" = "입력";
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "복구 키 입력";
"key_backup_recover_from_recovery_key_recover_action" = "기록 풀기";
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "복구 키를 잃어버렸나요? 설정에서 새 것을 설정할 수 있어요.";
"key_backup_recover_success_info" = "백업이 복구되었습니다!";
"key_backup_recover_done_action" = "끝";
"key_backup_setup_banner_title" = "절대 암호화된 메시지를 잃지 마세요";
"key_backup_setup_banner_subtitle" = "키 백업 시작";
"key_backup_recover_banner_title" = "절대 암호화된 메시지를 잃지 마세요";
"key_backup_recover_connent_banner_subtitle" = "이 기기를 키 백업에 연결";
"sign_out_existing_key_backup_alert_title" = "로그아웃하겠습니까?";
"sign_out_existing_key_backup_alert_sign_out_action" = "로그아웃";
"sign_out_non_existing_key_backup_alert_title" = "지금 로그아웃하면 암호화된 메시지에 접근할 수 없게 됩니다";
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "키 백업 시작";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "제 암호화된 메시지는 필요 없습니다";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "암호화된 메시지를 잃게 됩니다";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "로그아웃하기 전에 키를 백업하지 않으면 암호화된 메시지에 접근할 수 없게 됩니다.";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "로그아웃";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "백업";
"sign_out_key_backup_in_progress_alert_title" = "키 백업이 진행 중입니다. 지금 로그아웃하면 암호화된 메시지에 접근할 수 없게 됩니다.";
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "제 암호화된 메시지는 필요 없습니다";
"sign_out_key_backup_in_progress_alert_cancel_action" = "기다릴게요";
// MARK: - Device Verification
"device_verification_title" = "기기 확인";
"device_verification_security_advice" = "보안을 최대화하려면, 직접 또는 다른 신뢰할만한 통신 수단으로 확인하기를 추천합니다";
"device_verification_cancelled" = "상대방이 확인을 취소했습니다.";
"device_verification_cancelled_by_me" = "확인이 취소됬습니다. 이유: %@";
"device_verification_error_cannot_load_device" = "기기 정보를 불러올 수 없습니다.";
// Mark: Incoming
"device_verification_incoming_title" = "오는 확인 요청";
"device_verification_incoming_description_1" = "이 기기를 신뢰하도록 확인합니다. 상대방의 신뢰하는 기기는 종단간 암호화된 메시지를 사용할 때 더 마음에 안심을 줍니다.";
"device_verification_incoming_description_2" = "이 기기를 신뢰하도록 확인합니다, 그리고 상대방도 당신의 기기를 신뢰하도록 합니다.";
// MARK: Start
"device_verification_start_title" = "짧은 문장을 비교하여 확인";
"device_verification_start_wait_partner" = "상대방이 수락하기를 기다리는 중...";
"device_verification_start_use_legacy" = "아무것도 안 나타나나요? 일부 클라이언트는 아직 상호작용 확인을 지원하지 않습니다. 예전 확인 방식을 사용하세요.";
"device_verification_start_verify_button" = "확인 시작";
"device_verification_start_use_legacy_action" = "예전 확인 방식 사용";
// MARK: Verify
"device_verification_verify_title_emoji" = "상대방의 화면에 나타나는 다음 이모지를 확인하는 것으로 이 기기를 확인합니다";
"device_verification_verify_title_number" = "상대방의 화면에 나타나는 다음 숫자를 확인하는 것으로 이 기기를 확인합니다";
"device_verification_verify_wait_partner" = "상대방이 확인하기를 기다리는 중...";
// MARK: Verified
"device_verification_verified_title" = "확인되었습니다!";
"device_verification_verified_description_1" = "성공적으로 이 기기를 확인했습니다.";
"device_verification_verified_description_2" = "이 사용자와의 보안 메시지는 종단간 암호화되며 제 3자가 읽을 수 없습니다.";
"device_verification_verified_got_it_button" = "알겠습니다";
// MARK: Emoji
"device_verification_emoji_dog" = "개";
"device_verification_emoji_cat" = "고양이";
"device_verification_emoji_lion" = "사자";
"device_verification_emoji_horse" = "말";
"device_verification_emoji_unicorn" = "유니콘";
"device_verification_emoji_pig" = "돼지";
"device_verification_emoji_elephant" = "코끼리";
"device_verification_emoji_rabbit" = "토끼";
"device_verification_emoji_panda" = "판다";
"device_verification_emoji_rooster" = "수탉";
"device_verification_emoji_penguin" = "펭귄";
"device_verification_emoji_turtle" = "거북";
"device_verification_emoji_fish" = "물고기";
"device_verification_emoji_octopus" = "문어";
"device_verification_emoji_butterfly" = "나비";
"device_verification_emoji_flower" = "꽃";
"device_verification_emoji_tree" = "나무";
"device_verification_emoji_cactus" = "선인장";
"device_verification_emoji_mushroom" = "버섯";
"device_verification_emoji_globe" = "지구본";
"device_verification_emoji_moon" = "달";
"device_verification_emoji_cloud" = "구름";
"device_verification_emoji_fire" = "불";
"device_verification_emoji_banana" = "바나나";
"device_verification_emoji_apple" = "사과";
"device_verification_emoji_strawberry" = "딸기";
"device_verification_emoji_corn" = "옥수수";
"device_verification_emoji_pizza" = "피자";
"device_verification_emoji_cake" = "케이크";
"device_verification_emoji_heart" = "하트";
"device_verification_emoji_smiley" = "웃음";
"device_verification_emoji_robot" = "로봇";
"device_verification_emoji_hat" = "모자";
"device_verification_emoji_glasses" = "안경";
"device_verification_emoji_spanner" = "스패너";
"device_verification_emoji_santa" = "산타클로스";
"device_verification_emoji_thumbs up" = "좋아요";
"device_verification_emoji_umbrella" = "우산";
"device_verification_emoji_hourglass" = "모래시계";
"device_verification_emoji_clock" = "시계";
"device_verification_emoji_gift" = "선물";
"device_verification_emoji_light bulb" = "전구";
"device_verification_emoji_book" = "책";
"device_verification_emoji_pencil" = "연필";
"device_verification_emoji_paperclip" = "클립";
"device_verification_emoji_scissors" = "가위";
"device_verification_emoji_lock" = "자물쇠";
"device_verification_emoji_key" = "열쇠";
"device_verification_emoji_hammer" = "망치";
"device_verification_emoji_telephone" = "전화기";
"device_verification_emoji_flag" = "깃발";
"device_verification_emoji_train" = "기차";
"device_verification_emoji_bicycle" = "자전거";
"device_verification_emoji_aeroplane" = "비행기";
"device_verification_emoji_rocket" = "로켓";
"device_verification_emoji_trophy" = "트로피";
"device_verification_emoji_ball" = "공";
"device_verification_emoji_guitar" = "기타";
"device_verification_emoji_trumpet" = "트럼펫";
"device_verification_emoji_bell" = "종";
"device_verification_emoji_anchor" = "닻";
"device_verification_emoji_headphones" = "헤드폰";
"device_verification_emoji_folder" = "폴더";
"device_verification_emoji_pin" = "핀";
// MARK: File upload
"file_upload_error_title" = "파일 업로드";
"file_upload_error_unsupported_file_type_message" = "파일 형식을 지원하지 않습니다.";
// MARK: Emoji picker
"emoji_picker_title" = "리액션";
"emoji_picker_people_category" = "표정 & 사람";
"emoji_picker_nature_category" = "동물 & 자연";
"emoji_picker_foods_category" = "음식 & 음료";
"emoji_picker_activity_category" = "액티비티";
"emoji_picker_places_category" = "여행 & 명소";
"emoji_picker_objects_category" = "물건";
"emoji_picker_symbols_category" = "기호";
"emoji_picker_flags_category" = "깃발";
// MARK: Reaction history
"reaction_history_title" = "리액션";
"room_participants_remove_third_party_invite_prompt_msg" = "이 초대를 취소하겠습니까?";
// Errors
"error_user_already_logged_in" = "다른 홈서버에 연결합니다. 로그아웃하겠습니까?";
"room_accessiblity_scroll_to_bottom" = "아래로 스크롤";
"room_accessibility_search" = "검색";
"room_accessibility_integrations" = "통합";
"room_accessibility_upload" = "업로드";
"room_accessibility_call" = "전화";
"room_accessibility_hangup" = "끊기";
"media_type_accessibility_image" = "사진";
"media_type_accessibility_audio" = "소리";
"media_type_accessibility_video" = "영상";
"media_type_accessibility_location" = "위치";
"media_type_accessibility_file" = "파일";
"media_type_accessibility_sticker" = "스티커";
// Widget Picker
"widget_picker_title" = "통합";
"auth_add_email_message_2" = "계정 복구 용 이메일을 설정하세요, 이후 당신을 아는 사람들이 선택적으로 당신을 찾을 수 있도록 할 수 있습니다.";
"auth_add_phone_message_2" = "전화를 설정하세요, 이후 당신을 아는 사람들이 선택적으로 당신을 찾을 수 있도록 할 수 있습니다.";
"auth_add_email_phone_message_2" = "계정 복구 용 이메일을 설정하세요. 이후 당신을 아는 사람들이 선택적으로 당신을 찾을 수 있도록 이메일이나 전화를 사용할 수 있습니다.";
"auth_email_is_required" = "설정된 ID 서버가 없어서 이후 비밀번호를 초기화할 이메일 주소를 추가할 수 없습니다.";
"auth_phone_is_required" = "설정된 ID 서버가 없어서 이후 비밀번호를 초기화할 전화번호를 추가할 수 없습니다.";
"auth_reset_password_error_is_required" = "설정된 ID 서버가 없음: 비밀번호를 초기화하려면 서버 설정에서 하나를 추가하세요.";
"contacts_address_book_no_identity_server" = "설정된 ID 서버 없음";
"settings_discovery_settings" = "탐색";
"settings_identity_server_settings" = "ID 서버";
"settings_three_pids_management_information_part1" = "여기서 로그인하거나 계정을 복구하는데 사용할 이메일 주소나 전화번호를 관리하세요. 어디서 당신을 찾을 수 있는지 제어하세요 ";
"settings_three_pids_management_information_part2" = "탐색";
"settings_three_pids_management_information_part3" = ".";
"settings_calls_stun_server_fallback_button" = "대체 전화 지원 서버 허용";
"settings_calls_stun_server_fallback_description" = "홈서버가 전화 서버를 지원하지 않는다면 대체 전화 지원 서버 %@를 허용합니다 (전화하는 동안 IP 주소가 공유됩니다).";
"settings_devices_description" = "기기 공개 이름은 소통하는 사람들에게 보여집니다";
"settings_discovery_no_identity_server" = "현재 ID 서버를 사용하고 있지 않습니다. 연락처에 알고 있는 사람들이 당신을 찾을 수 있도록 하려면 서버를 하나 추가하세요.";
"settings_discovery_terms_not_signed" = "이메일이나 전화번호로 당신을 찾을 수 있도록 하려면 ID 서버 (%@)의 서비스 약관에 동의하세요.";
"settings_discovery_three_pids_management_information_part1" = "무슨 이메일 주소나 전화번호로 다른 사용자가 당신을 찾고 방에 초대할 수 있는지 관리하세요. 이 목록에서 이메일 주소나 전화번호를 추가하거나 제거할 수 있습니다. ";
"settings_discovery_three_pids_management_information_part2" = "사용자 설정";
"settings_discovery_three_pids_management_information_part3" = ".";
"settings_discovery_error_message" = "오류가 발생했습니다. 다시 시도해주세요.";
"settings_discovery_three_pid_details_title_email" = "이메일 관리";
"settings_discovery_three_pid_details_information_email" = "다른 사용자가 당신을 찾거나 방에 초대할 때 쓰이는 이메일 주소의 환경 설정을 관리합니다. 계정에 이메일 주소를 추가하거나 제거하세요.";
"settings_discovery_three_pid_details_title_phone_number" = "전화번호 관리";
"settings_discovery_three_pid_details_information_phone_number" = "다른 사용자가 당신을 찾거나 방에 초대할 때 쓰이는 전화번호의 환경 설정을 관리합니다. 계정에 전화번호를 추가하거나 제거하세요.";
"settings_discovery_three_pid_details_share_action" = "공유";
"settings_discovery_three_pid_details_revoke_action" = "취소";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "이메일 확인 취소";
"settings_discovery_three_pid_details_enter_sms_code_action" = "SMS 활성 코드 입력";
"settings_identity_server_description" = "위에 설정된 ID 서버를 사용하면, 알고 있는 연락처 사람들을 찾거나 연락처 사람들이 당신을 찾을 수 있습니다.";
"settings_identity_server_no_is" = "설정된 ID 서버 없음";
"settings_identity_server_no_is_description" = "현재 ID 서버를 사용하고 있지 않습니다. 알고 있는 연락처 사람들을 찾거나 연락처 사람들이 당신을 찾으려면 위의 서버 중 하나를 추가하세요.";
// Identity server settings
"identity_server_settings_title" = "ID 서버";
"identity_server_settings_description" = "알고 있는 연락처 사람들을 찾거나 연락처 사람들이 당신을 찾도록 현재 %@을(를) 사용하고 있습니다.";
"identity_server_settings_no_is_description" = "현재 ID 서버를 사용하고 있지 않습니다. 알고 있는 연락처 사람들을 찾거나 연락처 사람들이 당신을 찾도록 하려면, 위의 서버 중 하나를 추가하세요.";
"identity_server_settings_place_holder" = "ID 서버 입력";
"identity_server_settings_add" = "추가";
"identity_server_settings_change" = "바꾸기";
"identity_server_settings_disconnect_info" = "ID 서버와 연결을 해제하면 더 이상 다른 사용자가 당신을 찾을 수 없고, 다른 사용자를 이메일이나 전화번호로 초대할 수 없게 됩니다.";
"identity_server_settings_disconnect" = "연결 해제";
"identity_server_settings_alert_no_terms_title" = "ID 서버에 서비스 약관이 없습니다";
"identity_server_settings_alert_no_terms" = "선택한 ID 서버에는 서비스 약관이 없습니다. 서버의 소유자를 신뢰하는 경우에만 계속하세요.";
"identity_server_settings_alert_change_title" = "ID 서버 바꾸기";
"identity_server_settings_alert_change" = "ID 서버 %1$@을(를) 연결 해제하고 %2$@을(를) 연결합니까?";
"identity_server_settings_alert_disconnect_title" = "ID 서버 연결 해제";
"identity_server_settings_alert_disconnect" = "ID 서버 %@을(를) 연결 해제합니까?";
"identity_server_settings_alert_disconnect_button" = "연결 해제";
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "여전히 ID 서버 %@에 개인 정보를 공유하고 있습니다.\n\n연결을 해제하기 전에 ID 서버에서 이메일 주소와 전화번호를 제거하길 권합니다.";
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "무시하고 연결 해제";
"identity_server_settings_alert_error_terms_not_accepted" = "ID 서버로 설정하려면 %@ 약관에 동의해야 합니다.";
"identity_server_settings_alert_error_invalid_identity_server" = "%@은(는) 올바른 ID 서버가 아닙니다.";
"call_no_stun_server_error_title" = "잘못 설정된 서버 때문에 전화에 실패했습니다";
"call_no_stun_server_error_message_1" = "전화가 안정적으로 작동하게 하는 TURN 서버를 설정하려면 홈서버 %@의 관리자에게 연락하세요.";
"call_no_stun_server_error_message_2" = "아니면 %@에서 공개 서버를 사용할 수 있습니다, 하지만 그렇게 신뢰할 수는 없고 서버와 IP 주소를 공유하게 됩니다. 설정에서 이것을 관리할 수도 있습니다";
"call_no_stun_server_error_use_fallback_button" = "%@ 사용하기";
// Generic errors
"error_invite_3pid_with_no_identity_server" = "이메일로 초대하려면 설정에서 ID 서버를 추가하세요.";
"service_terms_modal_decline_button" = "끊기";
"service_terms_modal_description_for_identity_server_1" = "전화나 이메일로 다른 사람 찾기";
"service_terms_modal_description_for_identity_server_2" = "전화나 이메일로 다른 사람들이 찾을 수 있도록 하기";
// Service terms - Variant for identity server when displayed out of a context
"service_terms_modal_title_identity_server" = "연락처 탐색";
"service_terms_modal_message_identity_server" = "연락처를 탐색하려면 ID 서버 (%@)의 약관에 동의해야 합니다.";

View file

@ -18,4 +18,5 @@
"NSCameraUsageDescription" = "De camera wordt gebruikt om fotos en videos te maken, en voor videogesprekken.";
"NSPhotoLibraryUsageDescription" = "De fotogalerij wordt gebruikt om fotos en videos te versturen.";
"NSMicrophoneUsageDescription" = "De microfoon wordt gebruikt om videos te maken, en voor spraakoproepen.";
"NSContactsUsageDescription" = "Om u te kunnen tonen welke van uw contacten reeds Riot of Matrix gebruiken, kunnen we de e-mailadressen en telefoonnummers in uw adresboek naar uw Matrix-identiteitsserver sturen. New Vector bewaart deze gegevens niet en gebruikt ze niet voor andere doeleinden. Bekijk voor meer informatie de privacybeleidspagina in de instellingen van de app.";
"NSContactsUsageDescription" = "Om u te kunnen tonen welke van uw contacten reeds Matrix gebruiken, kan Riot de e-mailadressen en telefoonnummers in uw adresboek naar uw gekozen Matrix-identiteitsserver sturen. Waar mogelijk worden persoonlijke gegevens gehasht voor verzenden - bekijk het privacybeleid van uw identiteitsserver voor meer informatie.";
"NSCalendarsUsageDescription" = "Bekijk uw geplande afspraken in de app.";

View file

@ -42,8 +42,8 @@
"camera" = "Camera";
"voice" = "Spraak";
"video" = "Video";
"active_call" = "Actieve oproep";
"active_call_details" = "Actieve oproep (%@)";
"active_call" = "Oproep actief";
"active_call_details" = "Oproep actief (%@)";
"later" = "Later";
"rename" = "Hernoemen";
// Authentication
@ -64,7 +64,7 @@
"auth_repeat_password_placeholder" = "Wachtwoord herhalen";
"auth_repeat_new_password_placeholder" = "Bevestig uw nieuwe wachtwoord";
"auth_invalid_login_param" = "Onjuiste gebruikersnaam en/of wachtwoord";
"auth_invalid_user_name" = "Gebruikersnamen mogen alleen letters, cijfers, punten, afbreek- en lage streepjes bevatten";
"auth_invalid_user_name" = "Gebruikersnamen mogen alleen letters, cijfers, punten, koppeltekens en underscores bevatten";
"auth_invalid_password" = "Het wachtwoord is te kort (min 6)";
"auth_invalid_email" = "Dit ziet er niet uit als een geldig e-mailadres";
"auth_invalid_phone" = "Dit ziet er niet uit als een geldig telefoonnummer";
@ -441,7 +441,7 @@
"event_formatter_member_updates" = "%tu lidmaatschapsaanpassingen";
"bug_report_send" = "Versturen";
"auth_home_server_placeholder" = "URL (bv. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (bv. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (bv. https://vector.im)";
"room_ongoing_conference_call_with_close" = "Er is een vergadergesprek gaande. Neem deel met %@ of %@. %@ het.";
"room_ongoing_conference_call_close" = "Sluiten";
"room_conference_call_no_power" = "U heeft toestemming nodig om vergadergesprekken in dit groepsgesprek te beheren";
@ -556,7 +556,7 @@
"deactivate_account_informations_part1" = "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Dit zal er voor zorgen dat uw account alle gesprekken verlaat waar deze momenteel lid van is, en het verwijdert uw accountgegevens van de identiteitsserver. ";
"deactivate_account_informations_part2_emphasize" = "Deze actie is onomkeerbaar.";
"deactivate_account_informations_part3" = "\n\nHet deactiveren van uw account ";
"deactivate_account_informations_part4_emphasize" = "zal er niet standaard voor zorgen dat de berichten die u heeft verzonden worden vergeten. ";
"deactivate_account_informations_part4_emphasize" = "zal er standaard niet voor zorgen dat de berichten die u heeft verzonden worden vergeten. ";
"deactivate_account_informations_part5" = "Als u wilt dat wij de berichten vergeten, vinkt u het vakje hieronder aan.\n\nDe zichtbaarheid van berichten in Matrix is gelijkaardig aan e-mails. Het vergeten van uw berichten betekent dat berichten die u heeft verstuurd niet meer gedeeld worden met nieuwe of ongeregistreerde gebruikers, maar geregistreerde gebruikers die al toegang hebben tot deze berichten zullen alsnog toegang hebben tot hun eigen kopie ervan.";
"deactivate_account_forget_messages_information_part1" = "Vergeet alle berichten die ik heb verstuurd wanneer mijn account gedeactiveerd is (";
"deactivate_account_forget_messages_information_part3" = ": dit zal er voor zorgen dat toekomstige gebruikers een onvolledig beeld krijgen van gesprekken)";
@ -829,3 +829,29 @@
"emoji_picker_flags_category" = "Vlaggen";
// MARK: Reaction history
"reaction_history_title" = "Reacties";
"auth_forgot_password_error_no_configured_identity_server" = "Er is geen identiteitsserver geconfigureerd: voeg er een toe om uw wachtwoord opnieuw in te stellen.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen deelnemers toevoegen via e-mail.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen gesprek beginnen met een contact via e-mail.";
// Service terms
"service_terms_modal_title" = "Dienstvoorwaarden";
"service_terms_modal_message" = "Om door te gaan dient u de dienstvoorwaarden te aanvaarden.";
"service_terms_modal_accept_button" = "Aanvaarden";
"service_terms_modal_description_for_identity_server" = "Wees vindbaar voor anderen";
"service_terms_modal_description_for_integration_manager" = "Gebruik bots, bruggen, widgets en stickerpakketten";
// Errors
"error_user_already_logged_in" = "Het lijkt alsof u probeert te verbinden met een andere thuisserver. Wilt u zich afmelden?";
"room_participants_remove_third_party_invite_prompt_msg" = "Weet u zeker dat u deze uitnodiging wilt intrekken?";
"room_accessiblity_scroll_to_bottom" = "Scrollen naar onderen";
"room_accessibility_search" = "Zoeken";
"room_accessibility_integrations" = "Integraties";
"room_accessibility_upload" = "Uploaden";
"room_accessibility_call" = "Bellen";
"room_accessibility_hangup" = "Ophangen";
"media_type_accessibility_image" = "Afbeelding";
"media_type_accessibility_audio" = "Audio";
"media_type_accessibility_video" = "Video";
"media_type_accessibility_location" = "Locatie";
"media_type_accessibility_file" = "Bestand";
"media_type_accessibility_sticker" = "Sticker";
// Widget Picker
"widget_picker_title" = "Integraties";

View file

@ -2,3 +2,5 @@
"NSCameraUsageDescription" = "Aparat służy do robienia zdjęć i nagrywania filmów, prowadzenia rozmów wideo.";
"NSPhotoLibraryUsageDescription" = "Biblioteka zdjęć służy do wysyłania zdjęć i filmów.";
"NSMicrophoneUsageDescription" = "Mikrofon służy do robienia filmów, wykonywania połączeń.";
"NSContactsUsageDescription" = "Możemy pokazać Ci, które z Twoich kontaktów korzystają aktualnie z Riot, bądź Matrix. Możemy wysyłać adresy e-mail i numery telefonów z Twojej książki adresowej na Twój serwer Matrix. New Vector nie przechowuje Twoich danych, ani nie wykorzystuje ich w żadnym celu. Aby uzyskać dodatkowe informacje zajrzyj do zakładki \"polityka prywatności\" w ustawieniach aplikacji.";
"NSCalendarsUsageDescription" = "Zobacz swoje zaplanowane spotkania w aplikacji.";

View file

@ -1,5 +1,5 @@
/* New message from a specific person, not referencing a room */
"MSG_FROM_USER" = "Wiadomość od %@";
"MSG_FROM_USER" = "%@ wysłał(a) wiadomość";
/* New message from a specific person, not referencing a room. Content included. */
"MSG_FROM_USER_WITH_CONTENT" = "%@: %@";
/* New message from a specific person in a named room. Content included. */
@ -23,9 +23,9 @@
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nowych wiadomości w %@, %@ i innych";
/* New action message from a specific person, not referencing a room. */
"IMAGE_FROM_USER" = "%@ wysłał(a) Tobie zdjęcie %@";
"IMAGE_FROM_USER" = "%@ wysłał(a) zdjęcie %@";
/* New action message from a specific person in a named room. */
"IMAGE_FROM_USER_IN_ROOM" = "%@ wysłał(a) Tobie zdjęcie %@ w %@";
"IMAGE_FROM_USER_IN_ROOM" = "%@ wysłał(a) zdjęcie %@ w %@";
/* Multiple unread messages from a specific person, not referencing a room */
"MSGS_FROM_USER" = "%@ nowych wiadomości w %@";
/* Multiple messages in two rooms */
@ -36,3 +36,21 @@
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy grupowej";
/* A user has invited you to a chat */
"USER_INVITE_TO_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy";
/* Message title for a specific person in a named room */
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ w %@";
/* New message from a specific person in a named room */
"MSG_FROM_USER_IN_ROOM" = "%@ opublikowany w %@";
/* Sticker from a specific person, not referencing a room. */
"STICKER_FROM_USER" = "%@ wysłał(a) naklejkę";
/* Incoming one-to-one voice call */
"VOICE_CALL_FROM_USER" = "Zadzwonił do Ciebie z %@";
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "Połączenie wideo z %@";
/* Incoming unnamed voice conference invite from a specific person */
"VOICE_CONF_FROM_USER" = "Połączenie grupowe z %@";
/* Incoming unnamed video conference invite from a specific person */
"VIDEO_CONF_FROM_USER" = "Grupowe połączenie wideo z %@";
/* Incoming named voice conference invite from a specific person */
"VOICE_CONF_NAMED_FROM_USER" = "Połączenie grupowe z %@: '%@'";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Grupowe połączenie wideo z %@: '%@'";

View file

@ -526,3 +526,283 @@
"settings_key_backup_info_progress_done" = "Utworzono kopię zapasową wszystkich kluczy";
"settings_key_backup_button_delete" = "Usuń kopię zapasową";
"settings_key_backup_delete_confirmation_prompt_title" = "Usuń kopię zapasową";
"auth_login_single_sign_on" = "Zaloguj się za pomocą pojedynczego logowania";
"auth_autodiscover_invalid_response" = "Nieprawidłowa odpowiedź na wykrycie serwera domowego";
"settings_key_backup_button_create" = "Rozpocznij z użyciem klucza kopii zapasowej";
"settings_key_backup_button_restore" = "Przywróć z kopii zapasowej";
"room_details_fail_to_update_avatar" = "Nie udało się zaaktualizować zdjęcia pokoju";
"room_details_fail_to_update_room_name" = "Nie udało się zaaktualizować nazwy pokoju";
"room_details_fail_to_update_topic" = "Nie udało się zaaktualizować tematu";
"room_details_fail_to_update_room_guest_access" = "Nie udało się zaaktualizować dostępu gościa do pokoju";
"room_details_fail_to_update_room_join_rule" = "Nie udało się zaaktualizować reguły dołączania";
"room_details_fail_to_update_room_directory_visibility" = "Nie udało się zaaktualizować widoczności pokoju";
"room_details_fail_to_update_history_visibility" = "Nie udało się zaaktualizować historii widoczności";
"room_details_fail_to_add_room_aliases" = "Nie udało się dodać nowych adresów pokoi";
"room_details_fail_to_remove_room_aliases" = "Nie udało się usunąć adresów pokoi";
"room_details_fail_to_update_room_canonical_alias" = "Nie udało się zaaktualizować głównego adresu pokoju";
"room_details_fail_to_update_room_communities" = "Nie udało się zaaktualizować powiązanych społeczności";
"room_details_fail_to_update_room_direct" = "Nie udało się zaaktualizować flagi tego pokoju";
"room_details_fail_to_enable_encryption" = "Nie udało się włączyć szyfrowania w tym pokoju";
"group_details_home" = "Strona domowa";
"key_backup_setup_intro_setup_action_without_existing_backup" = "Zacznij korzystać z klucza bezpieczeństwa";
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Podłącz to urządzenie do klucza bezpieczeństwa";
"key_backup_setup_intro_manual_export_info" = "(Zaawansowany)";
"key_backup_setup_intro_manual_export_action" = "Ręcznie eksportuj klucze";
"key_backup_setup_passphrase_title" = "Zabezpiecz swoją kopię zapasową hasłem";
"key_backup_setup_passphrase_info" = "Będziemy przechowywać zaszyfrowaną kopię Twoich kluczy na naszym serwerze. Zabezpiecz swoją kopię zapasową przy pomocy hasła, aby zapewnić jej bezpieczeństwo.\n\nDla maksymalnego bezpieczeństwa - to powinno być inne hasło, niż hasło do konta.";
"key_backup_setup_passphrase_passphrase_title" = "Wejdź";
"key_backup_setup_passphrase_passphrase_placeholder" = "Wprowadź hasło";
"key_backup_setup_passphrase_passphrase_valid" = "Świetnie!";
"key_backup_setup_passphrase_passphrase_invalid" = "Spróbuj dodać słowo";
"key_backup_setup_passphrase_confirm_passphrase_title" = "Potwierdź";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Potwierdź hasło";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "Świetnie!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Hasło nie pasuje";
"key_backup_setup_passphrase_set_passphrase_action" = "Ustaw hasło";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Możesz też zabezpieczyć kopię zapasową przy pomocy klucza odzyskiwania, zapisując ją w bezpiecznym miejscu.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Zaawansowany) Konfiguruj za pomocą klucza odzyskiwania";
"key_backup_setup_success_title" = "Sukces!";
// Success from passphrase
"key_backup_setup_success_from_passphrase_info" = "Tworzone są kopie zapasowe kluczy.\n\nTwój klucz odzyskiwania to gwarant bezpieczeństwa - możesz użyć go, aby odzyskać dostęp do zaszyfrowanych wiadomości, jeśli zapomnisz hasła.\n\nTrzymaj ten klucz w bezpiecznym miejscu (np. w menedżerze haseł).";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Zapisz klucz odzyskiwania";
"key_backup_setup_success_from_passphrase_done_action" = "Zrobione";
// Success from recovery key
"key_backup_setup_success_from_recovery_key_info" = "Tworzone są kopie zapasowe kluczy.\n\nZrób kopię tego klucza i przechowuj ją w bezpiecznym miejscu.";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Klucz odzyskiwania";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Zrób kopię";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Zrobiłem kopię";
"key_backup_recover_title" = "Bezpieczne wiadomości";
"key_backup_recover_invalid_passphrase_title" = "Niepoprawne hasło odzyskiwania";
"key_backup_recover_invalid_passphrase" = "Nie można odszyfrować kopii zapasowej przy użyciu tego hasła: Proszę, sprawdź czy wprowadziłeś(aś) prawidłowe hasło odzyskiwania.";
"key_backup_recover_invalid_recovery_key_title" = "Klucz odzyskiwania nie pasuje";
"key_backup_recover_invalid_recovery_key" = "Nie można odszyfrować kopii zapasowej przy użycia tego klucza: Proszę, sprawdź czy wpisałeś go poprawnie.";
"key_backup_recover_from_passphrase_info" = "Skorzystaj ze swojego hasła odzyskiwania, aby odblokować historię bezpiecznych wiadomości";
"key_backup_recover_from_passphrase_passphrase_title" = "Wejdź";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Wprowadź hasło";
"key_backup_recover_from_passphrase_recover_action" = "Odblokuj historię";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Nie znasz hasła odzyskiwania? Możesz ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "Użyj swojego klucza odzyskiwania";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
"key_backup_recover_from_recovery_key_info" = "Skorzystaj z klucza odzyskiwania, aby odblokować historię bezpiecznych wiadomości";
"key_backup_recover_from_recovery_key_recovery_key_title" = "Wejdź";
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Wpisz klucz odzyskiwania";
"key_backup_recover_from_recovery_key_recover_action" = "Odblokuj historię";
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Zgubiłeś swój klucz odzyskiwania? Możesz ustawić nowy w ustawieniach.";
"key_backup_recover_success_info" = "Kopia zapasowa została przywrócona!";
"key_backup_recover_done_action" = "Zrobiono";
"key_backup_setup_banner_title" = "Nigdy nie trać zaszyfrowanych wiadomości";
"key_backup_setup_banner_subtitle" = "Zacznij korzystać z kopii zapasowej";
"key_backup_recover_banner_title" = "Nigdy nie trać zaszyfrowanych wiadomości";
"key_backup_recover_connent_banner_subtitle" = "Podłącz to urządzenie do kopii zapasowej";
"sign_out_existing_key_backup_alert_title" = "Jesteś pewien, że chcesz się wylogować?";
"sign_out_existing_key_backup_alert_sign_out_action" = "Wyloguj się";
"sign_out_non_existing_key_backup_alert_title" = "Jeśli wylogujesz się teraz, stracisz dostęp do swoich zaszyfrowanych wiadomości";
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Zacznij korzystać z kopii zapasowej";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Nie chcę swoich zaszyfrowanych wiadomości";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Utracisz zaszyfrowane wiadomości";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Utracisz dostęp do zaszyfrowanych wiadomości, jeżeli wylogujesz się przed utworzeniem kopii zapasowej.";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Wylogowanie";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Kopia zapasowa";
"sign_out_key_backup_in_progress_alert_title" = "Trwa tworzenie kopii zapasowej. Jeżeli wylogujesz się teraz - Stracisz dostęp do swoich zaszyfrowanych wiadomości.";
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Nie chcę moich zaszyfrowanych wiadomości";
"sign_out_key_backup_in_progress_alert_cancel_action" = "Poczekam";
// MARK: - Device Verification
"device_verification_title" = "Zweryfikuj urządzenie";
"device_verification_security_advice" = "Dla zapewnienia maksymalnego bezpieczeństwa zalecamy zrobienie tego samemu, bądź skorzystanie z innego, zaufanego środka komunikacji";
"device_verification_cancelled" = "Druga strona przerwała weryfikację.";
"device_verification_cancelled_by_me" = "Weryfikacja została przerwana. Powód: %@";
"device_verification_error_cannot_load_device" = "Nie można załadować informacji o urządzeniu.";
// Mark: Incoming
"device_verification_incoming_title" = "Przyszła prośba o weryfikację";
"device_verification_incoming_description_1" = "Zweryfikuj to urządzenie, aby oznaczyć je jako zaufane. Zaufane urządzenia dają Twoim rozmówcom dodatkowe poczucie spokoju, podczas konwersacji z włączonymi szyfrowanymi wiadomościami end-to-end.";
"device_verification_incoming_description_2" = "Weryfikacja tego urządzenia spowoduje oznaczenie go, jako zaufanego. Zostanie ono oznaczone jako zaufane również na urządzeniu Twojego rozmówcy.";
// MARK: Start
"device_verification_start_title" = "Zweryfikuj, porównując krótki tekst";
"device_verification_start_wait_partner" = "Oczekiwanie na zaakceptowanie przez rozmówce...";
"device_verification_start_use_legacy" = "Nic się nie pojawiło? Nie wszystkie wersje aplikacji obsługują jeszcze interaktywną weryfikację. Prosimy o skorzystanie ze starszej wersji.";
"device_verification_start_verify_button" = "Rozpocznij weryfikację";
"device_verification_start_use_legacy_action" = "Użyj starszej wersji weryfikacji";
// MARK: Verify
"device_verification_verify_title_emoji" = "Sprawdź to urządzenie potwierdzając, że następujące emotikony pojawiają się na ekranie rozmówcy";
"device_verification_verify_title_number" = "Sprawdź to urządzenie potwierdzając, że następujące liczby pojawiają się na ekranie rozmówcy";
"device_verification_verify_wait_partner" = "Oczekiwanie na rozmówce w celu potwierdzenia...";
// MARK: Verified
"device_verification_verified_title" = "Zweryfikowano!";
"device_verification_verified_description_1" = "Pomyślnie zweryfikowano to urządzenie.";
"device_verification_verified_description_2" = "Bezpieczne wiadomości z tym użytkownikiem są szyfrowane metodą end-to-end i nie mogą zostać odczytane przez osoby trzecie.";
"device_verification_verified_got_it_button" = "Zrobione";
// MARK: Emoji
"device_verification_emoji_dog" = "Pies";
"device_verification_emoji_cat" = "Kot";
"device_verification_emoji_lion" = "Lew";
"device_verification_emoji_horse" = "Koń";
"device_verification_emoji_unicorn" = "Jednorożec";
"device_verification_emoji_pig" = "Świnia";
"device_verification_emoji_elephant" = "Słoń";
"device_verification_emoji_rabbit" = "Królik";
"device_verification_emoji_panda" = "Panda";
"device_verification_emoji_rooster" = "Kogut";
"device_verification_emoji_penguin" = "Pingwin";
"device_verification_emoji_turtle" = "Żółw";
"device_verification_emoji_fish" = "Ryba";
"device_verification_emoji_octopus" = "Ośmiornica";
"device_verification_emoji_butterfly" = "Motyl";
"device_verification_emoji_flower" = "Kwiatek";
"device_verification_emoji_tree" = "Drzewo";
"device_verification_emoji_cactus" = "Kaktus";
"device_verification_emoji_mushroom" = "Grzyb";
"device_verification_emoji_globe" = "Kula ziemska";
"device_verification_emoji_moon" = "Księżyc";
"device_verification_emoji_cloud" = "Chmura";
"device_verification_emoji_fire" = "Ogień";
"device_verification_emoji_banana" = "Banan";
"device_verification_emoji_apple" = "Jabłko";
"device_verification_emoji_strawberry" = "Truskawka";
"device_verification_emoji_corn" = "Kukurydza";
"device_verification_emoji_pizza" = "Pizza";
"device_verification_emoji_cake" = "Ciasto";
"device_verification_emoji_heart" = "Serce";
"device_verification_emoji_smiley" = "Uśmiech";
"device_verification_emoji_robot" = "Robot";
"device_verification_emoji_hat" = "Kapelusz";
"device_verification_emoji_glasses" = "Okulary";
"device_verification_emoji_spanner" = "Klucz";
"device_verification_emoji_santa" = "Mikołaj";
"device_verification_emoji_thumbs up" = "Kciuk w górę";
"device_verification_emoji_umbrella" = "Parasolka";
"device_verification_emoji_hourglass" = "Klepsydra";
"device_verification_emoji_clock" = "Zegar";
"device_verification_emoji_gift" = "Prezent";
"device_verification_emoji_light bulb" = "Żarówka";
"device_verification_emoji_book" = "Książka";
"device_verification_emoji_pencil" = "Ołówek";
"device_verification_emoji_paperclip" = "Spinacz";
"device_verification_emoji_scissors" = "Nożyczki";
"device_verification_emoji_lock" = "Zamek";
"device_verification_emoji_key" = "Klucz";
"device_verification_emoji_hammer" = "Młotek";
"device_verification_emoji_telephone" = "Telefon";
"device_verification_emoji_flag" = "Flaga";
"device_verification_emoji_train" = "Pociąg";
"device_verification_emoji_bicycle" = "Rower";
"device_verification_emoji_aeroplane" = "Samolot";
"device_verification_emoji_rocket" = "Rakieta";
"device_verification_emoji_trophy" = "Trofeum";
"device_verification_emoji_ball" = "Piłka";
"device_verification_emoji_guitar" = "Gitara";
"device_verification_emoji_trumpet" = "Trąbka";
"device_verification_emoji_bell" = "Dzwon";
"device_verification_emoji_anchor" = "Kotwica";
"device_verification_emoji_headphones" = "Słuchawki";
"device_verification_emoji_folder" = "Folder";
"device_verification_emoji_pin" = "Przypnij";
// MARK: File upload
"file_upload_error_title" = "Wyślij plik";
"file_upload_error_unsupported_file_type_message" = "Typ pliku nie jest wspierany.";
// MARK: Emoji picker
"emoji_picker_title" = "Reakcje";
"emoji_picker_people_category" = "Emotikony i ludzie";
"emoji_picker_nature_category" = "Zwierzęta i przyroda";
"emoji_picker_foods_category" = "Jedzenie i picie";
"emoji_picker_activity_category" = "Aktywności";
"emoji_picker_places_category" = "Podróże i miejsca";
"emoji_picker_objects_category" = "Obiekty";
"emoji_picker_symbols_category" = "Symbole";
"emoji_picker_flags_category" = "Flagi";
// MARK: Reaction history
"reaction_history_title" = "Reakcje";
"auth_forgot_password_error_no_configured_identity_server" = "Brak skonfigurowanego serwera tożsamości: dodaj serwer tożsamości, aby zresetować hasło.";
"auth_softlogout_signed_out" = "Zostałeś wylogowany";
"auth_softlogout_reason" = "Administrator serwera domowego (%1$@) wylogował Cię z Twojego konta %2$@ (%3$@).";
"auth_softlogout_recover_encryption_keys" = "Zaloguj się aby odzyskać klucze szyfrujące zapisane na tym urządzeniu. Potrzebujesz ich aby odczytać wszystkie swoje zaszyfrowane wiadomości na którymkolwiek z Twoich urządzeń.";
"auth_softlogout_clear_data" = "Wyczyść prywatne dane";
"auth_softlogout_clear_data_message_1" = "Uwaga: Twoje prywatne dane (z uwzględnieniem kluczy szyfrujących) nadal znajdują się na tym urządzeniu.";
"auth_softlogout_clear_data_message_2" = "Wyczyść je, jeżeli nie będziesz już używać tego urządzenia, lub jeśli planujesz zalogować się na inne konto.";
"auth_softlogout_clear_data_sign_out_title" = "Czy jesteś pewien?";
"auth_softlogout_clear_data_sign_out_msg" = "Czy napewno chcesz wyczyścić wszystkie dane znajdujące się na tym urządzeniu? Zaloguj się ponownie, aby mieć dostęp do swojego konta, oraz wiadomości.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Serwer tożsamości nie został skonfigurowany, więc nie możesz dodać uczestnika przy użyciu adresu email.";
"room_participants_remove_third_party_invite_msg" = "Usuwanie zaproszeń nie jest wspierane przez obecną wersję API";
"room_participants_invited_section" = "ZAPROSZENI";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Serwer tożsamości nie został skonfigurowany, więc nie możesz rozpocząć rozmowy używając adresu email.";
"room_message_unable_open_link_error_message" = "Nie udało się otworzyć adresu.";
"room_conference_call_no_power" = "Potrzebujesz uprawnień, aby zarządzać połączeniami konferencyjnymi w tym pokoju";
"room_event_action_reaction_history" = "Historia reakcji";
"room_action_camera" = "Zrób zdjęcie lub nagraj wideo";
"settings_config_no_build_info" = "Brak informacji o buildzie";
"settings_labs_message_reaction" = "Odpowiadaj na wiadomości używając emoji";
"settings_key_backup_info" = "Zaszyfrowane wiadomości są zabezpieczone przy użyciu szyfrowania end-to-end. Tylko Ty oraz ich adresaci posiadają klucze do ich rozszyfrowania.";
"settings_key_backup_info_none" = "Twoje klucze z tego urządzenia nie posiadają kopii zapasowej.";
"settings_key_backup_info_signout_warning" = "Podłącz to urządzenie do klucza kopii zapasowej przed wylogowaniem się, aby nie utracić kluczy, które dostępne są tylko na tym urządzeniu.";
"settings_key_backup_info_valid" = "Kopia zapasowa twoich kluczy jest tworzona.";
"settings_key_backup_info_trust_signature_unknown" = "Kopia zapasowa posiada podpis urządzenia o ID: %@";
"settings_key_backup_info_trust_signature_valid" = "Kopia zapasowa posiada prawidłowy podpis tego urządzenia";
"settings_key_backup_info_trust_signature_valid_device_verified" = "Kopia zapasowa posiada prawidłowy podpis urządzenia %@";
"settings_key_backup_info_trust_signature_valid_device_unverified" = "Kopia zapasowa posiada podpis urządzenia %@";
"settings_key_backup_info_trust_signature_invalid_device_verified" = "Kopia zapasowa posiada nieprawidłowy podpis urządzenia %@";
"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Kopia zapasowa posiada nieprawidłowy podpis urządzenia %@";
"settings_key_backup_button_connect" = "Podłącz to urządzenie do kopii zapasowej kluczy";
"settings_key_backup_delete_confirmation_prompt_msg" = "Czy jesteś pewien? Stracisz dostęp do wszystkich swoich zaszyfrowanych wiadomości, jeżeli nie utworzyłeś poprawnej kopii zapasowej kluczy.";
"room_details_access_section" = "Kto może dołączyć do pokoju?";
"room_details_history_section_prompt_msg" = "Zmiany dostępu do historii będą miały zastosowanie tylko do przyszłych wiadomości w tym pokoju. Widoczność obecnych wiadomości pozostanie bez zmian.";
"settings_key_backup_info_not_valid" = "To urządzenie nie tworzy kopii zapasowej Twoich kluczy. Ciągle jednak masz istniejącą kopię zapasową, którą możesz przywrócić i w przyszłości uzupełnić.";
"room_details_addresses_disable_main_address_prompt_title" = "Główny adres ostrzega";
"room_details_addresses_disable_main_address_prompt_msg" = "Nie masz adresu głównego. Domyślny główny adres zostanie wybrany losowo";
"room_details_flair_section" = "Pokaż talent społecznościom";
"room_details_flair_invalid_id_prompt_msg" = "%@ nie jest prawidłowym identyfikatorem społeczności";
"group_participants_leave_prompt_msg" = "Czy jesteś pewien, że chcesz opuścić te grupę?";
"group_participants_remove_prompt_msg" = "Czy jesteś pewien, że chcesz usunąć %@ z tej grupy?";
"group_participants_invite_prompt_msg" = "Czy jesteś pewien, że chcesz zaprosić %@ do tej grupy?";
"group_participants_invite_malformed_id_title" = "Błąd. Nie udało się zaprosić";
"group_participants_invited_section" = "ZAPROSZONY";
"receipt_status_read" = "Czytaj: ";
// Media picker
"media_picker_title" = "Selektor mediów";
// Image picker
"image_picker_action_camera" = "Zrób zdjęcie";
"image_picker_action_library" = "Wybierz z biblioteki";
"directory_server_all_native_rooms" = "Wszystkie rodzime pokoje Matrix";
// Events formatter
"event_formatter_member_updates" = "%tu zmiany członkostwa";
"event_formatter_jitsi_widget_added" = "Konferencja VoIP dodana przez %@";
"event_formatter_jitsi_widget_removed" = "Konferencja VoIP usunięta przez %@";
"event_formatter_message_edited_mention" = "(edytowano)";
"network_offline_prompt" = "Wygląda na to, że nie masz połączenia z internetem.";
"homeserver_connection_lost" = "Nie można połączyć się z serwerem domowym.";
"public_room_section_title" = "Pokoje Publiczne (na %@):";
"bug_report_prompt" = "Poprzednio aplikacja uległa awarii. Czy chcesz wysłać raport?";
"rage_shake_prompt" = "Zauważyliśmy, że potrząsasz telefonem (czyżby ze zdenerwowania?). Chciałbyś nam zgłosić jakiś problem?";
"camera_access_not_granted" = "%@ nie ma uprawnień do korzystania z aparatu, zmień ustawienia w ustawieniach prywatności";
"camera_unavailable" = "Aparat jest niedostępny na Twoim urządzeniu";
"photo_library_access_not_granted" = "%@ nie ma uprawnień dostępu do biblioteki zdjęć, możesz to zmienić w ustawieniach prywatności";
"room_does_not_exist" = "%@ nie istnieje";
"call_already_displayed" = "Rozmowa tutaj już trwa.";
"call_jitsi_error" = "Nie udało się dołączyć do rozmowy konferencyjnej.";
"no_voip" = "%@ dzwoni do Ciebie ale %@ nie obsługuje jeszcze połączeń\nMożesz zignorować to powiadomienie i odebrać rozmowę na innym urządzeniu, bądź odrzucić je.";
// Crash report
"google_analytics_use_prompt" = "Chcesz pomóc nam w ulepszaniu %@ przez automatyczne wysyłanie anonimowych raportów o błędach i danych użytkowania?";
// Key backup wrong version
"e2e_key_backup_wrong_version_title" = "Nowy Klucz Kopii Zapasowej";
"room_participants_remove_third_party_invite_prompt_msg" = "Czy jesteś pewien, że chcesz odrzucić to zaproszenie?";
"e2e_key_backup_wrong_version" = "Wykryto nową wiadomość z kluczem zabezpieczeń. \nJeżeli to nie Ty - Prosimy, abyś zmienił swoje hasło w ustawieniach.";
"e2e_key_backup_wrong_version_button_settings" = "Ustawienia";
"e2e_key_backup_wrong_version_button_wasme" = "To ja";
"bug_report_description" = "Proszę opisać błąd. Co zrobiłeś? Co miało się stać? Co się stało?";
"bug_crash_report_description" = "Opisz, co zrobiłeś przed awarią:";
// Widget
"widget_no_integrations_server_configured" = "Nie skonfigurowano serwera integracji";
"widget_integrations_server_failed_to_connect" = "Błąd łączenia z serwerem integracji";
// Share extension
"share_extension_auth_prompt" = "Zaloguj się do głównej aplikacji, aby udostępniać zawartość";
"share_extension_failed_to_encrypt" = "Nie udało się wysłać. Sprawdź w głównej aplikacji ustawienia szyfrowania dla tego pokoju";
// Service terms
"service_terms_modal_title" = "Warunki usługi";
"service_terms_modal_message" = "Aby kontynuować musisz zaakceptować Warunki Usługi.";
"service_terms_modal_accept_button" = "Akceptuj";
"service_terms_modal_description_for_identity_server" = "Być wykrywalnym dla innych";
"service_terms_modal_description_for_integration_manager" = "Używaj Botów, mostków, widżetów i naklejek";
"key_backup_setup_title" = "Klucz kopii zapasowej";
"key_backup_setup_skip_alert_title" = "Jesteś pewien?";
"key_backup_setup_skip_alert_message" = "Możesz stracić wiadomości bezpieczeństwa, jeżeli wylogujesz się z, bądź stracisz swoje urządzenie.";
"key_backup_setup_skip_alert_skip_action" = "Pomiń";
"key_backup_setup_intro_title" = "Nigdy nie trać zaszyfrowanych wiadomości";
"key_backup_setup_intro_info" = "Wiadomości w szyfrowanym pokoju są zabezpieczone przez end-to-end. Tylko Ty i rozmówca macie klucze, aby odczytać te wiadomości.\nWykonaj kopię zapasową kluczy, aby ich nie zgubić.";

View file

@ -685,3 +685,146 @@
"room_action_reply" = "Ответ";
"settings_labs_message_reaction" = "Реагировать на сообщения с Emoji";
"settings_key_backup_button_connect" = "Подключите это устройство к ключу резервного копирования";
"close" = "Закрыть";
"auth_forgot_password_error_no_configured_identity_server" = "Сервер идентификации не настроен: добавьте один для сброса пароля.";
"auth_softlogout_signed_out" = "Вы вышли";
"auth_softlogout_sign_in" = "Войти";
"auth_softlogout_clear_data" = "Очистить личные данные";
"auth_softlogout_clear_data_button" = "Очистить все данные";
"auth_softlogout_clear_data_sign_out_title" = "Вы уверены?";
"auth_softlogout_clear_data_sign_out" = "Выйти";
"room_event_action_reaction_show_all" = "Показать все";
"room_event_action_reaction_show_less" = "Свернуть";
"room_event_action_reaction_history" = "История реакций";
"room_action_camera" = "Записать фото или видео";
"room_action_send_file" = "Отправить файл";
"room_message_edits_history_title" = "Редактирование сообщений";
// Media picker
"media_picker_title" = "Медиатека";
// Image picker
"image_picker_action_camera" = "Сфотографировать";
"image_picker_action_library" = "Выбрать из библиотеки";
"event_formatter_message_edited_mention" = "(отредактированный)";
"widget_integrations_server_failed_to_connect" = "Не удалось подключиться к серверу интеграции";
// Service terms
"service_terms_modal_title" = "Условия пользования";
"service_terms_modal_message" = "Для продолжения Вам необходимо принять Условия данного сервиса.";
"service_terms_modal_accept_button" = "Принять";
"service_terms_modal_description_for_identity_server" = "Быть открытыми для других";
"service_terms_modal_description_for_integration_manager" = "Используйте боты, мосты, виджеты и стикеры";
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Подключите это устройство к Key Backup";
// MARK: - Device Verification
"device_verification_title" = "Проверьте устройство";
"auth_softlogout_reason" = "Ваш домашний сервер (%1$@) админ выписал вас из вашей учетной записи %2$@ (%3$@).";
"auth_softlogout_recover_encryption_keys" = "Войдите, чтобы восстановить ключи шифрования, хранящиеся исключительно на этом устройстве. Они нужны вам для чтения всех ваших защищенных сообщений на любом устройстве.";
"auth_softlogout_clear_data_message_1" = "Внимание: Ваши личные данные (включая ключи шифрования) все еще хранятся на этом устройстве.";
"auth_softlogout_clear_data_message_2" = "Очистите его, если вы закончили использовать это устройство или хотите войти в другую учетную запись.";
"auth_softlogout_clear_data_sign_out_msg" = "Вы уверены, что хотите удалить все данные, хранящиеся в данный момент на этом устройстве? Войдите снова, чтобы получить доступ к данным и сообщениям вашей учетной записи.";
// Errors
"error_user_already_logged_in" = "Похоже, вы пытаетесь подключиться к другому серверу. Вы хотите выйти?";
"room_creation_error_invite_user_by_email_without_identity_server" = "Сервер идентификации не настроен, поэтому вы не можете добавить участника с электронной почтой.";
"room_participants_remove_third_party_invite_prompt_msg" = "Вы уверены, что хотите отозвать это приглашение?";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Сервер идентификации не настроен, поэтому вы не можете начать чат с контактом, используя электронную почту.";
"camera_unavailable" = "Камера недоступна на вашем устройстве";
"photo_library_access_not_granted" = "%@ не имеет доступа к библиотеке фотографий, измените настройки конфиденциальности";
// Widget
"widget_no_integrations_server_configured" = "Сервер интеграции не настроен";
"key_backup_recover_connent_banner_subtitle" = "Подключите это устройство к функции резервного копирования ключей";
"device_verification_security_advice" = "Для обеспечения максимальной безопасности мы рекомендуем делать это лично или использовать другие надежные средства связи";
"device_verification_cancelled" = "Другая сторона отменила проверку.";
"device_verification_cancelled_by_me" = "Проверка была отменена. Причина: %@";
"device_verification_error_cannot_load_device" = "Невозможно загрузить информацию об устройстве.";
// Mark: Incoming
"device_verification_incoming_title" = "Входящий запрос на проверку";
"device_verification_incoming_description_1" = "Проверьте это устройство, чтобы отметить его как доверенное. Доверительные устройства партнеров дают вам дополнительное спокойствие при использовании сообщений со сквозным шифрованием.";
"device_verification_incoming_description_2" = "Убедитесь, что данное устройство является доверенным, а также проверьте, что оно доверено для вашего партнера.";
// MARK: Start
"device_verification_start_title" = "Проверьте, сравнив короткую текстовую строку";
"device_verification_start_wait_partner" = "Ожидайте, пока партнер примет....";
"device_verification_start_use_legacy" = "Ничего не появляется? Не все клиенты пока поддерживают интерактивную проверку. Используйте устаревшую проверку.";
"device_verification_start_verify_button" = "Начать проверку";
"device_verification_verify_wait_partner" = "Ожидайте, пока партнер подтвердит....";
// MARK: Verified
"device_verification_verified_title" = "Проверено!";
"device_verification_verified_description_1" = "Вы успешно проверили это устройство.";
"device_verification_verified_got_it_button" = "Понял";
// MARK: Emoji
"device_verification_emoji_dog" = "Собака";
"device_verification_emoji_cat" = "Кот";
"device_verification_emoji_lion" = "Лев";
"device_verification_emoji_horse" = "Конь";
"device_verification_emoji_unicorn" = "Единорог";
"device_verification_emoji_pig" = "Хрюша";
"device_verification_emoji_elephant" = "Слон";
"device_verification_emoji_rabbit" = "Кролик";
"device_verification_emoji_panda" = "Панда";
"device_verification_emoji_rooster" = "Петух";
"device_verification_emoji_penguin" = "Пингвин";
"device_verification_emoji_turtle" = "Черепаха";
"device_verification_emoji_fish" = "Рыба";
"device_verification_emoji_octopus" = "Осьминог";
"device_verification_emoji_butterfly" = "Бабочка";
"device_verification_emoji_flower" = "Цветок";
"device_verification_emoji_tree" = "Дерево";
"device_verification_emoji_cactus" = "Кактус";
"device_verification_emoji_mushroom" = "Гриб";
"device_verification_emoji_globe" = "Глобус";
"device_verification_emoji_moon" = "Луна";
"device_verification_emoji_cloud" = "Облако";
"device_verification_emoji_fire" = "Огонь";
"device_verification_emoji_banana" = "Банан";
"device_verification_emoji_apple" = "Яблоко";
"device_verification_emoji_strawberry" = "Клубника";
"device_verification_emoji_corn" = "Кукуруза";
"device_verification_emoji_pizza" = "Пицца";
"device_verification_emoji_cake" = "Печенье";
"device_verification_emoji_heart" = "Сердце";
"device_verification_emoji_smiley" = "Улыбка";
"device_verification_emoji_robot" = "Робот";
"device_verification_emoji_hat" = "Шапка";
"device_verification_emoji_glasses" = "Очки";
"device_verification_emoji_spanner" = "Ключ";
"device_verification_emoji_santa" = "Морозко";
"device_verification_emoji_thumbs up" = "Палец вверх";
"device_verification_emoji_umbrella" = "Зонт";
"device_verification_emoji_hourglass" = "Песочные часы";
"device_verification_emoji_clock" = "Часы";
"device_verification_emoji_gift" = "Подарок";
"device_verification_emoji_light bulb" = "Лампочка";
"device_verification_emoji_book" = "Книга";
"device_verification_emoji_pencil" = "Карандаш";
"device_verification_emoji_paperclip" = "Скрепка";
"device_verification_emoji_scissors" = "Ножницы";
"device_verification_emoji_lock" = "Замок";
"device_verification_emoji_key" = "Ключ";
"device_verification_emoji_hammer" = "Молоток";
"device_verification_emoji_telephone" = "Телефон";
"device_verification_emoji_flag" = "Флаг";
"device_verification_emoji_train" = "Поезд";
"device_verification_emoji_bicycle" = "Велосипед";
"device_verification_emoji_aeroplane" = "Самолет";
"device_verification_emoji_rocket" = "Ракета";
"device_verification_emoji_trophy" = "Кубок";
"device_verification_emoji_ball" = "Мяч";
"device_verification_emoji_guitar" = "Гитара";
"device_verification_emoji_trumpet" = "Труба";
"device_verification_emoji_bell" = "Колокольчик";
"device_verification_emoji_anchor" = "Якорь";
"device_verification_emoji_headphones" = "Наушники";
"device_verification_emoji_folder" = "Папка";
"device_verification_emoji_pin" = "Ручка";
// MARK: File upload
"file_upload_error_title" = "Файл загрузить";
"file_upload_error_unsupported_file_type_message" = "Тип файла не поддерживается.";
// MARK: Emoji picker
"emoji_picker_title" = "Реакция";
"emoji_picker_people_category" = "Смайлики & люди";
"emoji_picker_nature_category" = "Животные & Природа";
"emoji_picker_foods_category" = "Еда & напитки";
"emoji_picker_activity_category" = "Aктивность";
"emoji_picker_places_category" = "Путешествия & Места";
"emoji_picker_objects_category" = "Объекты";
"emoji_picker_symbols_category" = "Символы";
"emoji_picker_flags_category" = "Флаги";
// MARK: Reaction history
"reaction_history_title" = "Реакции";

View file

@ -2,4 +2,5 @@
"NSCameraUsageDescription" = "Kamera përdoret për të bërë foto dhe regjistruar video, dhe për të bërë thirrje video.";
"NSPhotoLibraryUsageDescription" = "Fototeka përdoret për të dërguar foto dhe video.";
"NSMicrophoneUsageDescription" = "Mikrofoni përdoret për të regjistruar video, dhe për të bërë thirrje.";
"NSContactsUsageDescription" = "Që të mund tju shfaqim se cilët prej kontakteve tuaj përdorin tashmë Riot ose Matrix, mund të dërgojmë adresat email dhe numrat e telefonave nga libri juaj i adresave te Shërbyesi Matrix i Identiteteve. Vektori i ri nuk i depoziton këto të dhëna, as i përdor për ndonjë qëllim tjetër. Për më tepër të dhëna, shihni faqen e rregullave të privatësisë, te rregullimet e aplikacionit.";
"NSContactsUsageDescription" = "Që të mund tju shfaqim se cilët prej kontakteve tuaj përdorin tashmë Riot ose Matrix, mund të dërgojmë adresat email dhe numrat e telefonave nga libri juaj i adresave te shërbyesi Matrix i identiteteve. Vektori i ri nuk i depoziton këto të dhëna, as i përdor për ndonjë qëllim tjetër. Për më tepër të dhëna, shihni faqen e rregullave të privatësisë, te rregullimet e aplikacionit.";
"NSCalendarsUsageDescription" = "Shihini te aplikacioni takimet tuaja të planifikuara.";

View file

@ -746,7 +746,7 @@
"device_verification_emoji_santa" = "Babagjyshi i Vitit të Ri";
"device_verification_emoji_umbrella" = "Ombrellë";
"device_verification_emoji_hourglass" = "Klepsidër";
"device_verification_emoji_clock" = "Klasë";
"device_verification_emoji_clock" = "Orë";
"device_verification_emoji_gift" = "Dhuratë";
"device_verification_emoji_light bulb" = "Llambë";
"device_verification_emoji_book" = "Libër";
@ -770,7 +770,7 @@
"device_verification_emoji_anchor" = "Spirancë";
"device_verification_emoji_headphones" = "Kufje";
"device_verification_emoji_folder" = "Dosje";
"event_formatter_message_edited_mention" = "(U përpunua)";
"event_formatter_message_edited_mention" = "(u përpunua)";
// Widget
"widget_no_integrations_server_configured" = "Ska të formësuar shërbyes integrimesh";
"widget_integrations_server_failed_to_connect" = "Su arrit të lidhej me shërbyes integrimesh";
@ -778,3 +778,66 @@
"room_event_action_reaction_show_all" = "Shfaqi krejt";
"room_event_action_reaction_show_less" = "Shfaq më pak";
"room_message_edits_history_title" = "Përpunime mesazhi";
"auth_softlogout_signed_out" = "Keni bërë dalje";
"auth_softlogout_sign_in" = "Hyni";
"auth_softlogout_reason" = "Përgjegjësi i shërbyesit tuaj Home (%1$@) ka bërë daljen tuaj nga llogaria juaj %2$@ (%3$@).";
"auth_softlogout_recover_encryption_keys" = "Bëni hyrjen, që të rimerrni kyçe fshehtëzimi të depozituar përjashtimisht në këtë pajisje. Do tju duhen për të lexuar krejt mesazhet tuaj të siguruar në çfarëdo pajisje.";
"auth_softlogout_clear_data" = "Spastro të dhëna personale";
"auth_softlogout_clear_data_message_1" = "Kujdes: Të dhënat tuaja personale (përfshi kyçe fshehtëzimi) janë ende të depozituara në këtë pajisje.";
"auth_softlogout_clear_data_message_2" = "Spastrojini, nëse keni përfunduar së përdoruri këtë pajisje, ose dëshironi të hyni në një llogari tjetër.";
"auth_softlogout_clear_data_button" = "Spastro krejt të dhënat";
"auth_softlogout_clear_data_sign_out_title" = "Jeni i sigurt?";
"auth_softlogout_clear_data_sign_out_msg" = "Jeni i sigurt se doni të spastrohen krejt të dhënat e depozituara në këtë pajisje? Që të mund të hyni te të dhëna të llogarisë tuaj dhe te mesazhe, bëni sërish hyrjen.";
"auth_softlogout_clear_data_sign_out" = "Dilni";
"room_event_action_reaction_history" = "Historik reagimesh";
"room_action_camera" = "Bëni foto ose video";
"room_action_send_file" = "Dërgoni kartelë";
// Media picker
"media_picker_title" = "Mediatekë";
// Image picker
"image_picker_action_camera" = "Bëni një foto";
"image_picker_action_library" = "Zgjidhni prej biblioteke";
"camera_unavailable" = "Kamera në pajisjen tuaj sështë e përdorshme";
"photo_library_access_not_granted" = "%@ ska leje të hyjë në fototekë, ju lutemi, ndryshoni rregullimet e privatësisë";
"device_verification_emoji_spanner" = "Çelës";
"device_verification_emoji_lock" = "Dry";
"device_verification_emoji_pin" = "Fiksoje";
// MARK: File upload
"file_upload_error_title" = "Ngarkim kartele";
"file_upload_error_unsupported_file_type_message" = "Lloj i pambuluar kartele.";
// MARK: Emoji picker
"emoji_picker_title" = "Reagime";
"emoji_picker_people_category" = "Emotikone & Njerëz";
"emoji_picker_nature_category" = "Kafshë & Natyrë";
"emoji_picker_foods_category" = "Ushqim & Pije";
"emoji_picker_activity_category" = "Veprimtari";
"emoji_picker_places_category" = "Udhëtim & Vende";
"emoji_picker_objects_category" = "Objekte";
"emoji_picker_symbols_category" = "Simbole";
"emoji_picker_flags_category" = "Flamuj";
// MARK: Reaction history
"reaction_history_title" = "Reagime";
"auth_forgot_password_error_no_configured_identity_server" = "Ska shërbyes identitetesh të formësuar: shtoni një të tillë që të ricaktoni fjalëkalimin tuaj.";
"room_creation_error_invite_user_by_email_without_identity_server" = "Ska shërbyes identitetesh të formësua, ndaj smund të shtoni një pjesëmarrës me një email.";
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Ska shërbyes identitetesh të formësuar, ndaj smund të nisni një fjalosje me një kontakt duke përdorur një email.";
// Service terms
"service_terms_modal_title" = "Kushte Shërbimi";
"service_terms_modal_message" = "Që të vazhdohet, lypset të pranoni Kushtet e këtij shërbimi.";
"service_terms_modal_accept_button" = "Pranoji";
"service_terms_modal_description_for_identity_server" = "Jini i zbulueshëm nga të tjerët";
"service_terms_modal_description_for_integration_manager" = "Përdorni Robotë, ura, widget-e dhe paketa ngjitësish";
"room_participants_remove_third_party_invite_prompt_msg" = "Jeni i sigurt se doni të shfuqizohet kjo ftesë?";
// Errors
"error_user_already_logged_in" = "Duket se po rrekeni të lidheni me një tjetër shërbyes Home. Doni të dilet nga llogaria?";
"room_accessiblity_scroll_to_bottom" = "Rrëshqitni drejt fundit";
"room_accessibility_search" = "Kërko";
"room_accessibility_integrations" = "Integrime";
"room_accessibility_upload" = "Ngarkoni";
"room_accessibility_call" = "Thirrje";
"room_accessibility_hangup" = "Mbylle";
"media_type_accessibility_image" = "Figurë";
"media_type_accessibility_audio" = "Audio";
"media_type_accessibility_video" = "Video";
"media_type_accessibility_location" = "Vendndodhje";
"media_type_accessibility_file" = "Kartelë";
"media_type_accessibility_sticker" = "Ngjitës";

View file

@ -0,0 +1 @@

View file

@ -2,5 +2,5 @@
"NSCameraUsageDescription" = "De camera wor gebruukt vo fotootjes te trekkn en filmtjes te moakn, en vo videogesprekkn.";
"NSPhotoLibraryUsageDescription" = "De fotogalerie wor gebruukt vo fotootjes en filmtjes te versteurn.";
"NSMicrophoneUsageDescription" = "De microfoon wor gebruukt vo filmtjes te maken, en vo sproakiproepn.";
"NSContactsUsageDescription" = "Vo je te kunn toogn dewelkse van je contactn dat al Riot of Matrix gebruukn, kunn we de-mailadressn en telefongnumeros in jen adresboek noa je Matrix-identiteitsserver steurn. New Vector bewoart deze gegevens nie en gebruukt ze ook nie voor andere doeleindn. Bekykt vo meer informoatie de privacybeleidspagina in dinstelliengn van den app.";
"NSContactsUsageDescription" = "Vo je te kunn toogn dewelkse van je contactn dat al Matrix gebruukn, kun Riot de-mailadressn en telefongnumeros in jen adresboek noa je gekoozn Matrix-identiteitsserver steurn. Woar meuglik worden persoonlike gegeevns gehasht vo dan ze worden verzonden - bekyk t privacybeleid van jen identiteitsserver vo meer informoasje.";
"NSCalendarsUsageDescription" = "Bekykt je geplande afsproakn in den app.";

View file

@ -10,3 +10,88 @@
// Actions
"view" = "Toogn";
"next" = "Volgende";
"store_full_description" = "Communiceert ip joun manier.\n\nE chat-app, onder joun controle en zeer flexibel. Riot lat je communiceern lyk of da je wilt. Gemakt vo [matrix] - de standoard voor open, gedecentraliseerde communicoasje.\n\nMakt e gratis account aan ip matrix.org, verkrygt jen eigen server ip https://modular.im, of gebruukt een andere Matrix-server.\n\nVowa zoun kik vo Riot.im kiezn?\n\n• VOLLEDIGE COMMUNICOASJE: makt gesprekkn an rond je teams, je moats, je gemeenschap - gelyk of da je gy t mo wilt! Chat, deelt bestandn, voegt widgets toe en makt stem- en video-iproepn - ollemoale volledig gratis.\n\n• KRACHTIGE INTEGROASJE: gebruukt Riot.im met de hulpmiddeln woamee da je vertrouwd zyt. Me Riot.im ku je zelfst chattn me gebrukers en groepn ip andere chat-apps.\n\n• PRIVÉ EN VEILIG: houdt je gesprekkn geheim. End-tout-end-versleuterienge van de bovenste plank zorgt dervoorn da je privécommunicoasje ook privé bluuft.\n\n• OPEN, NIE GESLOOTN: vrye software, gebouwd ip Matrix. Zy den boas over jen eigen gegeevns deur jen eigen server te gebruukn, of te kiezn voor een andere server da je vertrouwt.\n\n• WOA DA JOOK ZYT: houdt contact woa da jook zyt me volledig gesynchroniseerde berichtgeschiedenisse ip al je toestelln, en online ip https://riot.im.";
"back" = "Weere";
"continue" = "Verdergoan";
"create" = "Anmoakn";
"start" = "Beginn";
"leave" = "Verloatn";
"remove" = "Verwydern";
"invite" = "Uutnodign";
"retry" = "Herprobeern";
"on" = "An";
"off" = "Uut";
"cancel" = "Annuleern";
"save" = "Ipsloan";
"join" = "Toetreedn";
"decline" = "Afwyzn";
"accept" = "Anveirdn";
"preview" = "Voorvertoogn";
"camera" = "Camera";
"voice" = "Sproak";
"video" = "Video";
"active_call" = "Iproep actief";
"active_call_details" = "Iproep actief (%@)";
"later" = "Later";
"rename" = "Hernoemn";
"collapse" = "toeplooin";
"send_to" = "Stuurt noa %@";
"sending" = "Wor versteurd";
"close" = "Sluutn";
// Authentication
"auth_login" = "Anmeldn";
"auth_register" = "Registreern";
"auth_submit" = "Versteurn";
"auth_skip" = "Oversloan";
"auth_login_single_sign_on" = "Anmeldn met enkele anmeldienge";
"auth_send_reset_email" = "Herstelmail versteurn";
"auth_return_to_login" = "Weere noa t anmeldiengsscherm";
"auth_user_id_placeholder" = "E-mailadresse of gebrukersnoame";
"auth_password_placeholder" = "Paswoord";
"auth_new_password_placeholder" = "Nieuw paswoord";
"auth_user_name_placeholder" = "Gebrukersnoame";
"auth_optional_email_placeholder" = "E-mailadresse (optioneel)";
"auth_email_placeholder" = "E-mailadresse";
"auth_optional_phone_placeholder" = "Telefongnumero (optioneel)";
"auth_phone_placeholder" = "Telefongnumero";
"auth_repeat_password_placeholder" = "Paswoord herhoaln";
"auth_repeat_new_password_placeholder" = "Bevestigt je nieuw paswoord";
"auth_home_server_placeholder" = "URL (bv. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (bv. https://matrix.org)";
"auth_invalid_login_param" = "Verkeerd(e) gebrukersnoame en/of paswoord";
"auth_invalid_user_name" = "Gebrukersnoamn meugn alleene mo letters, cyfers, puntn, koppelteekns en underscores bevattn";
"auth_invalid_password" = "t Paswoord is te kort (min 6)";
"auth_invalid_email" = "Da ziet der hier nie uut gelyk e geldig e-mailadresse";
"auth_invalid_phone" = "Da ziet der hier nie uut gelyk e geldige telefongnumero";
"auth_missing_password" = "Paswoord mankeert";
"auth_add_email_message" = "Voegt een e-mailadresse toe an jen account zoda gebrukers je kunn viendn en zoda je je paswoord kut herstelln.";
"auth_add_phone_message" = "Voegt e telefongnumero toe an jen account zoda gebrukers je kunn viendn.";
"auth_add_email_phone_message" = "Voegt een e-mailadresse en/of e telefongnumero toe an jen account zoda gebrukers je kunn viendn. Jen e-mailadresse mak et ook meuglik van je paswoord therstelln.";
"auth_add_email_and_phone_message" = "Voegt een e-mailadresse en e telefongnumero toe an jen account zoda gebrukers je kunn viendn. Jen e-mailadresse mak et ook meuglik van je paswoord therstelln.";
"auth_missing_email" = "E-mailadresse mankeert";
"auth_missing_phone" = "Telefongnumero mankeert";
"auth_missing_email_or_phone" = "E-mailadresse of telefongnumero mankeert";
"auth_email_in_use" = "Dat e-mailadresse hier is al in gebruuk";
"auth_phone_in_use" = "Dien telefongnumero hier is al in gebruuk";
"auth_untrusted_id_server" = "Den identiteitsserver is nie vertrouwd";
"auth_password_dont_match" = "De paswoordn kommn nie overeen";
"auth_username_in_use" = "De gebrukersnoame is al in gebruuk";
"auth_forgot_password" = "Paswoord vergeetn?";
"auth_forgot_password_error_no_configured_identity_server" = "t Is genen identiteitsserver geconfigureerd gewist: voegt der enen toe vo je paswoord herin te stelln.";
"auth_email_not_found" = "E-mail versteurn mislukt: dat e-mailadresse is hier nie gevoundn";
"auth_use_server_options" = "Angepaste serverinstelliengn gebruukn (geavanceerd)";
"auth_email_validation_message" = "Bekykt jen e-mail vo vorts te goan me de registroasje";
"auth_msisdn_validation_title" = "Verificoasje in afwachtienge";
"auth_msisdn_validation_message" = "Mèn een smse met een activoasjecode gesteurd ghed. Voert die code hieroundern in.";
"auth_msisdn_validation_error" = "Kostege den telefongnumero nie verifieern.";
"auth_recaptcha_message" = "Dezen thuusserver wilt der geern zeker van zyn da je gy geen robot zyt";
"auth_reset_password_message" = "Gif t e-mailadresse da me jen account verbondn is in vo je paswoord herin te stelln:";
"auth_reset_password_missing_email" = "t E-mailadresse da men jen account verbondn is moet ingegeevn wordn.";
"auth_reset_password_missing_password" = "t Moet e nieuw paswoord ingegeevn wordn.";
"auth_reset_password_email_validation_message" = "t Is een e-mail noa %@ gesteurd gewist. Klikt hierounder van zodra da je de koppelienge derin geopend ghed èt.";
"auth_reset_password_next_step_button" = "k Èn ik mn e-mailadresse geverifieerd";
"auth_reset_password_error_unauthorized" = "Verifieern van t e-mailadresse is mislukt: zorgt da jip de koppelienge in den e-mail geklikt ghed èt";
"auth_reset_password_error_not_found" = "t Ziet der nie noar uut da jen e-mailadresse met e Matrix-ID ip dezen thuusserver is verboundn.";
"auth_reset_password_success_message" = "Je paswoord is heringesteld gewist.\n\nJe zy nu ip alle toestelln afgemeld en je goa geen pushmeldiengn nie mi ontvangn. Vo meldiengn herin te schoakeln, meld je jen ip ieder toestel heran.";
"auth_softlogout_signed_out" = "Je zyt afgemeld";
"auth_softlogout_sign_in" = "Anmeldn";

View file

@ -39,6 +39,7 @@ internal enum RiotDefaults {
internal static let showRedactionsInRoomHistory: Bool = _document["showRedactionsInRoomHistory"]
internal static let showUnsupportedEventsInRoomHistory: Bool = _document["showUnsupportedEventsInRoomHistory"]
internal static let sortRoomMembersUsingLastSeenTime: Bool = _document["sortRoomMembersUsingLastSeenTime"]
internal static let stunServerFallback: String = _document["stunServerFallback"]
internal static let syncLocalContacts: Bool = _document["syncLocalContacts"]
internal static let webAppUrl: String = _document["webAppUrl"]
internal static let webAppUrlBeta: String = _document["webAppUrlBeta"]

View file

@ -92,6 +92,21 @@ internal enum StoryboardScene {
internal static let initialScene = InitialSceneType<Riot.RoomContextualMenuViewController>(storyboard: RoomContextualMenuViewController.self)
}
internal enum ServiceTermsModalScreenViewController: StoryboardType {
internal static let storyboardName = "ServiceTermsModalScreenViewController"
internal static let initialScene = InitialSceneType<Riot.ServiceTermsModalScreenViewController>(storyboard: ServiceTermsModalScreenViewController.self)
}
internal enum SettingsDiscoveryThreePidDetailsViewController: StoryboardType {
internal static let storyboardName = "SettingsDiscoveryThreePidDetailsViewController"
internal static let initialScene = InitialSceneType<Riot.SettingsDiscoveryThreePidDetailsViewController>(storyboard: SettingsDiscoveryThreePidDetailsViewController.self)
}
internal enum SettingsIdentityServerViewController: StoryboardType {
internal static let storyboardName = "SettingsIdentityServerViewController"
internal static let initialScene = InitialSceneType<Riot.SettingsIdentityServerViewController>(storyboard: SettingsIdentityServerViewController.self)
}
internal enum SimpleScreenTemplateViewController: StoryboardType {
internal static let storyboardName = "SimpleScreenTemplateViewController"

View file

@ -30,25 +30,21 @@ internal enum VectorL10n {
internal static var authAcceptPolicies: String {
return VectorL10n.tr("Vector", "auth_accept_policies")
}
/// Add an email address and a phone number to your account to let users discover you. Email address will also let you reset your password.
internal static var authAddEmailAndPhoneMessage: String {
return VectorL10n.tr("Vector", "auth_add_email_and_phone_message")
}
/// Registration with email and phone number at once is not supported yet until the api exists. Only the phone number will be taken into account. You may add your email to your profile in settings.
internal static var authAddEmailAndPhoneWarning: String {
return VectorL10n.tr("Vector", "auth_add_email_and_phone_warning")
}
/// Add an email address to your account to let users discover you, and to reset your password.
internal static var authAddEmailMessage: String {
return VectorL10n.tr("Vector", "auth_add_email_message")
/// Set an email for account recovery, and later to be optionally discoverable by people who know you.
internal static var authAddEmailMessage2: String {
return VectorL10n.tr("Vector", "auth_add_email_message_2")
}
/// Add an email address and/or a phone number to your account to let users discover you. Email address will also let you reset your password.
internal static var authAddEmailPhoneMessage: String {
return VectorL10n.tr("Vector", "auth_add_email_phone_message")
/// Set an email for account recovery. Use later email or phone to be optionally discoverable by people who know you.
internal static var authAddEmailPhoneMessage2: String {
return VectorL10n.tr("Vector", "auth_add_email_phone_message_2")
}
/// Add a phone number to your account to let users discover you.
internal static var authAddPhoneMessage: String {
return VectorL10n.tr("Vector", "auth_add_phone_message")
/// Set a phone, and later to be optionally discoverable by people who know you.
internal static var authAddPhoneMessage2: String {
return VectorL10n.tr("Vector", "auth_add_phone_message_2")
}
/// Invalid homeserver discovery response
internal static var authAutodiscoverInvalidResponse: String {
@ -58,6 +54,10 @@ internal enum VectorL10n {
internal static var authEmailInUse: String {
return VectorL10n.tr("Vector", "auth_email_in_use")
}
/// No identity server is configured so you cannot add an email address in order to reset your password in the future.
internal static var authEmailIsRequired: String {
return VectorL10n.tr("Vector", "auth_email_is_required")
}
/// Failed to send email: This email address was not found
internal static var authEmailNotFound: String {
return VectorL10n.tr("Vector", "auth_email_not_found")
@ -74,11 +74,15 @@ internal enum VectorL10n {
internal static var authForgotPassword: String {
return VectorL10n.tr("Vector", "auth_forgot_password")
}
/// No identity server is configured: add one to reset your password.
internal static var authForgotPasswordErrorNoConfiguredIdentityServer: String {
return VectorL10n.tr("Vector", "auth_forgot_password_error_no_configured_identity_server")
}
/// URL (e.g. https://matrix.org)
internal static var authHomeServerPlaceholder: String {
return VectorL10n.tr("Vector", "auth_home_server_placeholder")
}
/// URL (e.g. https://matrix.org)
/// URL (e.g. https://vector.im)
internal static var authIdentityServerPlaceholder: String {
return VectorL10n.tr("Vector", "auth_identity_server_placeholder")
}
@ -162,6 +166,10 @@ internal enum VectorL10n {
internal static var authPhoneInUse: String {
return VectorL10n.tr("Vector", "auth_phone_in_use")
}
/// No identity server is configured so you cannot add a phone number in order to reset your password in the future.
internal static var authPhoneIsRequired: String {
return VectorL10n.tr("Vector", "auth_phone_is_required")
}
/// Phone number
internal static var authPhonePlaceholder: String {
return VectorL10n.tr("Vector", "auth_phone_placeholder")
@ -186,6 +194,10 @@ internal enum VectorL10n {
internal static func authResetPasswordEmailValidationMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "auth_reset_password_email_validation_message", p1)
}
/// No identity server is configured: add one in server options to reset your password.
internal static var authResetPasswordErrorIsRequired: String {
return VectorL10n.tr("Vector", "auth_reset_password_error_is_required")
}
/// 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")
@ -366,6 +378,22 @@ internal enum VectorL10n {
internal static var callJitsiError: String {
return VectorL10n.tr("Vector", "call_jitsi_error")
}
/// Please ask the administrator of your homeserver %@ to configure a TURN server in order for calls to work reliably.
internal static func callNoStunServerErrorMessage1(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_message_1", p1)
}
/// Alternatively, you can try to use the public server at %@, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings
internal static func callNoStunServerErrorMessage2(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_message_2", p1)
}
/// Call failed due to misconfigured server
internal static var callNoStunServerErrorTitle: String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_title")
}
/// Try using %@
internal static func callNoStunServerErrorUseFallbackButton(_ p1: String) -> String {
return VectorL10n.tr("Vector", "call_no_stun_server_error_use_fallback_button", p1)
}
/// Camera
internal static var camera: String {
return VectorL10n.tr("Vector", "camera")
@ -398,6 +426,10 @@ internal enum VectorL10n {
internal static var contactsAddressBookNoContact: String {
return VectorL10n.tr("Vector", "contacts_address_book_no_contact")
}
/// No identity server configured
internal static var contactsAddressBookNoIdentityServer: String {
return VectorL10n.tr("Vector", "contacts_address_book_no_identity_server")
}
/// You didn't allow Riot to access your local contacts
internal static var contactsAddressBookPermissionDenied: String {
return VectorL10n.tr("Vector", "contacts_address_book_permission_denied")
@ -962,6 +994,10 @@ internal enum VectorL10n {
internal static var encryptedRoomMessageReplyToPlaceholder: String {
return VectorL10n.tr("Vector", "encrypted_room_message_reply_to_placeholder")
}
/// Add an identity server in your settings to invite by email.
internal static var errorInvite3pidWithNoIdentityServer: String {
return VectorL10n.tr("Vector", "error_invite_3pid_with_no_identity_server")
}
/// It looks like youre trying to connect to another homeserver. Do you want to sign out?
internal static var errorUserAlreadyLoggedIn: String {
return VectorL10n.tr("Vector", "error_user_already_logged_in")
@ -1118,6 +1154,82 @@ internal enum VectorL10n {
internal static var homeserverConnectionLost: String {
return VectorL10n.tr("Vector", "homeserver_connection_lost")
}
/// Add
internal static var identityServerSettingsAdd: String {
return VectorL10n.tr("Vector", "identity_server_settings_add")
}
/// Disconnect from the identity server %1$@ and connect to %2$@ instead?
internal static func identityServerSettingsAlertChange(_ p1: String, _ p2: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_change", p1, p2)
}
/// Change identity server
internal static var identityServerSettingsAlertChangeTitle: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_change_title")
}
/// Disconnect from the identity server %@?
internal static func identityServerSettingsAlertDisconnect(_ p1: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_disconnect", p1)
}
/// Disconnect
internal static var identityServerSettingsAlertDisconnectButton: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_disconnect_button")
}
/// You are still sharing your personal data on the identity server %@.\n\nWe recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.
internal static func identityServerSettingsAlertDisconnectStillSharing3pid(_ p1: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_disconnect_still_sharing_3pid", p1)
}
/// Disconnect anyway
internal static var identityServerSettingsAlertDisconnectStillSharing3pidButton: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_disconnect_still_sharing_3pid_button")
}
/// Disconnect identity server
internal static var identityServerSettingsAlertDisconnectTitle: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_disconnect_title")
}
/// %@ is not a valid identity server.
internal static func identityServerSettingsAlertErrorInvalidIdentityServer(_ p1: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_error_invalid_identity_server", p1)
}
/// You must accept terms of %@ to set it as identity server.
internal static func identityServerSettingsAlertErrorTermsNotAccepted(_ p1: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_error_terms_not_accepted", p1)
}
/// The identity server you have chosen does not have any terms of service. Only continue if you trust the owner of the server.
internal static var identityServerSettingsAlertNoTerms: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_no_terms")
}
/// Identity server has no terms of services
internal static var identityServerSettingsAlertNoTermsTitle: String {
return VectorL10n.tr("Vector", "identity_server_settings_alert_no_terms_title")
}
/// Change
internal static var identityServerSettingsChange: String {
return VectorL10n.tr("Vector", "identity_server_settings_change")
}
/// You are currently using %@ to discover and be discoverable by existing contacts you know.
internal static func identityServerSettingsDescription(_ p1: String) -> String {
return VectorL10n.tr("Vector", "identity_server_settings_description", p1)
}
/// Disconnect
internal static var identityServerSettingsDisconnect: String {
return VectorL10n.tr("Vector", "identity_server_settings_disconnect")
}
/// Disconnecting from your identity server will mean you wont be discoverable by other users and be able to invite others by email or phone.
internal static var identityServerSettingsDisconnectInfo: String {
return VectorL10n.tr("Vector", "identity_server_settings_disconnect_info")
}
/// You are not currently using an identity server. To discover and be discoverable by existing contacts, add one above.
internal static var identityServerSettingsNoIsDescription: String {
return VectorL10n.tr("Vector", "identity_server_settings_no_is_description")
}
/// Enter an identity server
internal static var identityServerSettingsPlaceHolder: String {
return VectorL10n.tr("Vector", "identity_server_settings_place_holder")
}
/// Identity Server
internal static var identityServerSettingsTitle: String {
return VectorL10n.tr("Vector", "identity_server_settings_title")
}
/// Take photo
internal static var imagePickerActionCamera: String {
return VectorL10n.tr("Vector", "image_picker_action_camera")
@ -1550,6 +1662,10 @@ internal enum VectorL10n {
internal static var roomCreationAppearancePicture: String {
return VectorL10n.tr("Vector", "room_creation_appearance_picture")
}
/// No identity server is configured so you cannot add a participant with an email.
internal static var roomCreationErrorInviteUserByEmailWithoutIdentityServer: String {
return VectorL10n.tr("Vector", "room_creation_error_invite_user_by_email_without_identity_server")
}
/// Search / invite by User ID, Name or email
internal static var roomCreationInviteAnotherUser: String {
return VectorL10n.tr("Vector", "room_creation_invite_another_user")
@ -2166,9 +2282,13 @@ internal enum VectorL10n {
internal static var roomParticipantsRemovePromptTitle: String {
return VectorL10n.tr("Vector", "room_participants_remove_prompt_title")
}
/// Remove third-party invite is not supported yet until the api exists
internal static var roomParticipantsRemoveThirdPartyInviteMsg: String {
return VectorL10n.tr("Vector", "room_participants_remove_third_party_invite_msg")
/// Are you sure you want to revoke this invite?
internal static var roomParticipantsRemoveThirdPartyInvitePromptMsg: String {
return VectorL10n.tr("Vector", "room_participants_remove_third_party_invite_prompt_msg")
}
/// No identity server is configured so you cannot start a chat with a contact using an email.
internal static var roomParticipantsStartNewChatErrorUsingUserEmailWithoutIdentityServer: String {
return VectorL10n.tr("Vector", "room_participants_start_new_chat_error_using_user_email_without_identity_server")
}
/// Participants
internal static var roomParticipantsTitle: String {
@ -2394,6 +2514,42 @@ internal enum VectorL10n {
internal static var sending: String {
return VectorL10n.tr("Vector", "sending")
}
/// Accept
internal static var serviceTermsModalAcceptButton: String {
return VectorL10n.tr("Vector", "service_terms_modal_accept_button")
}
/// Decline
internal static var serviceTermsModalDeclineButton: String {
return VectorL10n.tr("Vector", "service_terms_modal_decline_button")
}
/// Find others by phone or email
internal static var serviceTermsModalDescriptionForIdentityServer1: String {
return VectorL10n.tr("Vector", "service_terms_modal_description_for_identity_server_1")
}
/// Be found by phone or email
internal static var serviceTermsModalDescriptionForIdentityServer2: String {
return VectorL10n.tr("Vector", "service_terms_modal_description_for_identity_server_2")
}
/// Use Bots, bridges, widgets and sticker packs
internal static var serviceTermsModalDescriptionForIntegrationManager: String {
return VectorL10n.tr("Vector", "service_terms_modal_description_for_integration_manager")
}
/// To continue you need to accept the terms of this service (%@).
internal static func serviceTermsModalMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "service_terms_modal_message", p1)
}
/// Accept the terms of the identity server (%@) to discover contacts.
internal static func serviceTermsModalMessageIdentityServer(_ p1: String) -> String {
return VectorL10n.tr("Vector", "service_terms_modal_message_identity_server", p1)
}
/// Terms Of Service
internal static var serviceTermsModalTitle: String {
return VectorL10n.tr("Vector", "service_terms_modal_title")
}
/// Contact discovery
internal static var serviceTermsModalTitleIdentityServer: String {
return VectorL10n.tr("Vector", "service_terms_modal_title_identity_server")
}
/// Add email address
internal static var settingsAddEmailAddress: String {
return VectorL10n.tr("Vector", "settings_add_email_address")
@ -2414,6 +2570,14 @@ internal enum VectorL10n {
internal static var settingsCallsSettings: String {
return VectorL10n.tr("Vector", "settings_calls_settings")
}
/// Allow fallback call assist server
internal static var settingsCallsStunServerFallbackButton: String {
return VectorL10n.tr("Vector", "settings_calls_stun_server_fallback_button")
}
/// Allow fallback call assist server %@ when your homeserver does not offer one (your IP address would be shared during a call).
internal static func settingsCallsStunServerFallbackDescription(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_calls_stun_server_fallback_description", p1)
}
/// Change password
internal static var settingsChangePassword: String {
return VectorL10n.tr("Vector", "settings_change_password")
@ -2470,11 +2634,11 @@ internal enum VectorL10n {
internal static var settingsCryptoDeviceId: String {
return VectorL10n.tr("Vector", "settings_crypto_device_id")
}
/// \nDevice key:
/// \nDevice key:\n
internal static var settingsCryptoDeviceKey: String {
return VectorL10n.tr("Vector", "settings_crypto_device_key")
}
/// Device name:
/// Device Public Name:
internal static var settingsCryptoDeviceName: String {
return VectorL10n.tr("Vector", "settings_crypto_device_name")
}
@ -2498,6 +2662,70 @@ internal enum VectorL10n {
internal static var settingsDevices: String {
return VectorL10n.tr("Vector", "settings_devices")
}
/// A device's public name is visible to people you communicate with
internal static var settingsDevicesDescription: String {
return VectorL10n.tr("Vector", "settings_devices_description")
}
/// An error occured. Please retry.
internal static var settingsDiscoveryErrorMessage: String {
return VectorL10n.tr("Vector", "settings_discovery_error_message")
}
/// You are not currently using an identity server. To be discoverable by existing contacts you known, add one.
internal static var settingsDiscoveryNoIdentityServer: String {
return VectorL10n.tr("Vector", "settings_discovery_no_identity_server")
}
/// DISCOVERY
internal static var settingsDiscoverySettings: String {
return VectorL10n.tr("Vector", "settings_discovery_settings")
}
/// Agree to the Identity Server (%@) Terms of Service to allow yourself to be discoverable by email address or phone number.
internal static func settingsDiscoveryTermsNotSigned(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_discovery_terms_not_signed", p1)
}
/// Cancel email validation
internal static var settingsDiscoveryThreePidDetailsCancelEmailValidationAction: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_cancel_email_validation_action")
}
/// Enter SMS activation code
internal static var settingsDiscoveryThreePidDetailsEnterSmsCodeAction: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_enter_sms_code_action")
}
/// Manage preferences for this email address, which other users can use to discover you and use to invite you to rooms. Add or remove email addresses in Accounts.
internal static var settingsDiscoveryThreePidDetailsInformationEmail: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_information_email")
}
/// Manage preferences for this phone number, which other users can use to discover you and use to invite you to rooms. Add or remove phone numbers in Accounts.
internal static var settingsDiscoveryThreePidDetailsInformationPhoneNumber: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_information_phone_number")
}
/// Revoke
internal static var settingsDiscoveryThreePidDetailsRevokeAction: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_revoke_action")
}
/// Share
internal static var settingsDiscoveryThreePidDetailsShareAction: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_share_action")
}
/// Manage email
internal static var settingsDiscoveryThreePidDetailsTitleEmail: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_title_email")
}
/// Manage phone number
internal static var settingsDiscoveryThreePidDetailsTitlePhoneNumber: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pid_details_title_phone_number")
}
/// Manage which email addresses or phone numbers other users can use to discover you and use to invite you to rooms. Add or remove email addresses or phone numbers from this list in
internal static var settingsDiscoveryThreePidsManagementInformationPart1: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pids_management_information_part1")
}
/// User Settings
internal static var settingsDiscoveryThreePidsManagementInformationPart2: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pids_management_information_part2")
}
/// .
internal static var settingsDiscoveryThreePidsManagementInformationPart3: String {
return VectorL10n.tr("Vector", "settings_discovery_three_pids_management_information_part3")
}
/// Display Name
internal static var settingsDisplayName: String {
return VectorL10n.tr("Vector", "settings_display_name")
@ -2542,6 +2770,22 @@ internal enum VectorL10n {
internal static func settingsGlobalSettingsInfo(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_global_settings_info", p1)
}
/// Using the identity server set above, you can discover and be discoverable by existing contacts you know.
internal static var settingsIdentityServerDescription: String {
return VectorL10n.tr("Vector", "settings_identity_server_description")
}
/// No identity server configured
internal static var settingsIdentityServerNoIs: String {
return VectorL10n.tr("Vector", "settings_identity_server_no_is")
}
/// You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one above.
internal static var settingsIdentityServerNoIsDescription: String {
return VectorL10n.tr("Vector", "settings_identity_server_no_is_description")
}
/// IDENTITY SERVER
internal static var settingsIdentityServerSettings: String {
return VectorL10n.tr("Vector", "settings_identity_server_settings")
}
/// IGNORED USERS
internal static var settingsIgnoredUsers: String {
return VectorL10n.tr("Vector", "settings_ignored_users")
@ -2778,6 +3022,18 @@ internal enum VectorL10n {
internal static var settingsThirdPartyNotices: String {
return VectorL10n.tr("Vector", "settings_third_party_notices")
}
/// Manage which email addresses or phone numbers you can use to log in or recover your account here. Control who can find you in
internal static var settingsThreePidsManagementInformationPart1: String {
return VectorL10n.tr("Vector", "settings_three_pids_management_information_part1")
}
/// Discovery
internal static var settingsThreePidsManagementInformationPart2: String {
return VectorL10n.tr("Vector", "settings_three_pids_management_information_part2")
}
/// .
internal static var settingsThreePidsManagementInformationPart3: String {
return VectorL10n.tr("Vector", "settings_three_pids_management_information_part3")
}
/// Settings
internal static var settingsTitle: String {
return VectorL10n.tr("Vector", "settings_title")

View file

@ -30,6 +30,8 @@ final class RiotSettings: NSObject {
static let notificationsShowDecryptedContent = "showDecryptedContent"
static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif"
static let pinRoomsWithUnreadMessages = "pinRoomsWithUnread"
static let allowStunServerFallback = "allowStunServerFallback"
static let stunServerFallback = "stunServerFallback"
}
/// Riot Standard Room Member Power Level
@ -119,4 +121,24 @@ final class RiotSettings: NSObject {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
}
}
// MARK: Calls
/// Indicate if `allowStunServerFallback` settings has been set once.
var isAllowStunServerFallbackHasBeenSetOnce: Bool {
return UserDefaults.standard.object(forKey: UserDefaultsKeys.allowStunServerFallback) != nil
}
var allowStunServerFallback: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.allowStunServerFallback)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.allowStunServerFallback)
}
}
var stunServerFallback: String? {
return UserDefaults.standard.string(forKey: UserDefaultsKeys.stunServerFallback)
}
}

View file

@ -108,5 +108,6 @@ class DarkTheme: NSObject, Theme {
func applyStyle(onButton button: UIButton) {
// NOTE: Tint color does nothing by default on button type `UIButtonType.custom`
button.tintColor = self.tintColor
button.setTitleColor(self.tintColor, for: .normal)
}
}

View file

@ -108,5 +108,6 @@ class DefaultTheme: NSObject, Theme {
func applyStyle(onButton button: UIButton) {
// NOTE: Tint color does nothing by default on button type `UIButtonType.custom`
button.tintColor = self.tintColor
button.setTitleColor(self.tintColor, for: .normal)
}
}

View file

@ -56,10 +56,12 @@ typedef enum : NSUInteger
WidgetManagerErrorCodeNotEnoughPower,
WidgetManagerErrorCodeCreationFailed,
WidgetManagerErrorCodeNoIntegrationsServerConfigured,
WidgetManagerErrorCodeFailedToConnectToIntegrationsServer
WidgetManagerErrorCodeFailedToConnectToIntegrationsServer,
WidgetManagerErrorCodeTermsNotSigned
}
WidgetManagerErrorCode;
FOUNDATION_EXPORT NSString *const WidgetManagerErrorOpenIdTokenKey;
/**
The `WidgetManager` helps to handle modular widgets.

View file

@ -513,6 +513,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
MXHTTPOperation *operation;
NSString *userId = mxSession.myUser.userId;
NSLog(@"[WidgetManager] registerForScalarToken");
WidgetManagerConfig *config = [self configForUser:userId];
if (!config.hasUrls)
{
@ -537,16 +539,23 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
NSString *scalarToken;
MXJSONModelSetString(scalarToken, JSONResponse[@"scalar_token"])
config.scalarToken = scalarToken;
config.scalarToken = scalarToken;
self->configs[userId] = config;
[self saveConfigs];
// Validate it (this mostly checks to see if the IM needs us to agree to some terms)
MXHTTPOperation *operation3 = [self validateScalarToken:scalarToken forMXSession:mxSession complete:^(BOOL valid) {
if (success)
{
success(scalarToken);
}
} failure:failure];
[operation mutateTo:operation3];
} failure:^(NSError *error) {
NSLog(@"[WidgetManager] registerForScalarToken: Failed to register. Error: %@", error);
@ -621,9 +630,24 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
complete(NO);
}
else if (failure)
{
MXError *mxError = [[MXError alloc] initWithNSError:error];
if ([mxError.errcode isEqualToString:kMXErrCodeStringTermsNotSigned])
{
NSLog(@"[WidgetManager] validateScalarToke. Error: Need to accept terms");
NSError *termsNotSignedError = [NSError errorWithDomain:WidgetManagerErrorDomain
code:WidgetManagerErrorCodeTermsNotSigned
userInfo:@{
NSLocalizedDescriptionKey:error.userInfo[NSLocalizedDescriptionKey]
}];
failure(termsNotSignedError);
}
else
{
failure(error);
}
}
}];
}

View file

@ -36,6 +36,38 @@ class WidgetManagerConfig: NSObject, NSCoding {
}
}
var baseUrl: NSString? {
// Same comment as https://github.com/matrix-org/matrix-react-sdk/blob/1b0d8510a2ee93beddcd34c2d5770aa9fc76b1d9/src/ScalarAuthClient.js#L108
// The terms endpoints are new and so live on standard _matrix prefixes,
// but IM rest urls are currently configured with paths, so remove the
// path from the base URL before passing it to the js-sdk
// We continue to use the full URL for the calls done by
// Riot-iOS, but the standard terms API called
// by the matrix-ios-sdk lives on the standard _matrix path. This means we
// don't support running IMs on a non-root path, but it's the only
// realistic way of transitioning to _matrix paths since configs in
// the wild contain bits of the API path.
// Once we've fully transitioned to _matrix URLs, we can give people
// a grace period to update their configs, then use the rest url as
// a regular base url.
guard let apiUrl = self.apiUrl as String?, let imApiUrl = URL(string: apiUrl) else {
return nil
}
guard var baseUrl = URL(string: "/", relativeTo: imApiUrl)?.absoluteString else {
return nil
}
if baseUrl.hasSuffix("/") {
// SDK doest not like trailing /
baseUrl = String(baseUrl.dropLast())
}
return baseUrl as NSString
}
init(apiUrl: NSString?, uiUrl: NSString?) {
self.apiUrl = apiUrl
self.uiUrl = uiUrl

View file

@ -51,6 +51,8 @@
AuthFallBackViewController *authFallBackViewController;
}
@property (nonatomic, readonly) BOOL isIdentityServerConfigured;
@end
@implementation AuthenticationViewController
@ -191,6 +193,11 @@
[forgotPasswordTitle addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.tintColor range:NSMakeRange(0, forgotPasswordTitle.length)];
[self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateNormal];
[self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateHighlighted];
NSMutableAttributedString *forgotPasswordTitleDisabled = [[NSMutableAttributedString alloc] initWithAttributedString:forgotPasswordTitle];
[forgotPasswordTitleDisabled addAttribute:NSForegroundColorAttributeName value:[ThemeService.shared.theme.tintColor colorWithAlphaComponent:0.3] range:NSMakeRange(0, forgotPasswordTitle.length)];
[self.forgotPasswordButton setAttributedTitle:forgotPasswordTitleDisabled forState:UIControlStateDisabled];
[self updateForgotPwdButtonVisibility];
NSAttributedString *serverOptionsTitle = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_use_server_options", @"Vector", nil) attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor, NSFontAttributeName: [UIFont systemFontOfSize:14]}];
@ -259,6 +266,11 @@
autoDiscovery = nil;
}
- (BOOL)isIdentityServerConfigured
{
return self.identityServerTextField.text.length > 0;
}
- (void)setAuthType:(MXKAuthenticationType)authType
{
if (self.authType == MXKAuthenticationTypeRegister)
@ -655,10 +667,23 @@
[self hideCustomServers:!self.customServersContainer.hidden];
}
else if (sender == self.forgotPasswordButton)
{
if (!self.isIdentityServerConfigured)
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"error"]
message:NSLocalizedStringFromTable(@"auth_forgot_password_error_no_configured_identity_server", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
return;
}
else
{
// Update UI to reset password
self.authType = MXKAuthenticationTypeForgotPassword;
}
}
else if (sender == self.rightBarButtonItem)
{
// Check whether a request is in progress
@ -735,7 +760,6 @@
// Show the supported 3rd party ids which may be added to the account
authInputsview.thirdPartyIdentifiersHidden = NO;
[self updateRegistrationScreenWithThirdPartyIdentifiersHidden:NO];
}
}];
@ -1013,6 +1037,10 @@
{
[self setHomeServerTextFieldText:customHomeServerURL];
}
else
{
[self checkIdentityServer];
}
NSString *customIdentityServerURL = [[NSUserDefaults standardUserDefaults] objectForKey:@"customIdentityServerURL"];
if (customIdentityServerURL.length)
{

View file

@ -516,6 +516,20 @@
if (restClient)
{
MXWeakify(self);
[self checkIdentityServerRequirement:restClient success:^(BOOL identityServerRequired) {
MXStrongifyAndReturnIfNil(self);
if (identityServerRequired && !restClient.identityServer)
{
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain
code:0
userInfo:@{
NSLocalizedDescriptionKey:[NSBundle mxk_localizedStringForKey:@"auth_phone_is_required"]
}]);
return;
}
// Check whether a second 3pid is available
_isThirdPartyIdentifierPending = (!self.emailContainer.isHidden && self.emailTextField.text.length && ![self isFlowCompleted:kMXLoginFlowTypeEmailIdentity]);
@ -586,6 +600,11 @@
}];
} failure:^(NSError *error) {
callback(nil, error);
}];
// Async response
return;
}
@ -606,34 +625,65 @@
if (restClient)
{
MXWeakify(self);
[self checkIdentityServerRequirement:restClient success:^(BOOL identityServerRequired) {
MXStrongifyAndReturnIfNil(self);
if (identityServerRequired && !restClient.identityServer)
{
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain
code:0
userInfo:@{
NSLocalizedDescriptionKey:[NSBundle mxk_localizedStringForKey:@"auth_email_is_required"]
}]);
return;
}
// Check whether a second 3pid is available
_isThirdPartyIdentifierPending = (nbPhoneNumber && ![self isFlowCompleted:kMXLoginFlowTypeMSISDN]);
self->_isThirdPartyIdentifierPending = (self->nbPhoneNumber && ![self isFlowCompleted:kMXLoginFlowTypeMSISDN]);
// Launch email validation
submittedEmail = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:self.emailTextField.text];
self->submittedEmail = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:self.emailTextField.text];
NSString *identityServer = restClient.identityServer;
// Create the next link that is common to all Vector.im clients
NSString *nextLink = [NSString stringWithFormat:@"%@/#/register?client_secret=%@&hs_url=%@&is_url=%@&session_id=%@",
NSString *nextLink = [NSString stringWithFormat:@"%@/#/register?client_secret=%@&hs_url=%@&session_id=%@",
[Tools webAppUrl],
[submittedEmail.clientSecret stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]],
[self->submittedEmail.clientSecret stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]],
[restClient.homeserver stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]],
[restClient.identityServer stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]],
[currentSession.session stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]];
[self->currentSession.session stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]];
[submittedEmail requestValidationTokenWithMatrixRestClient:restClient
if (identityServer)
{
nextLink = [NSString stringWithFormat:@"%@&is_url=%@", nextLink,
[identityServer stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]];
}
[self->submittedEmail requestValidationTokenWithMatrixRestClient:restClient
isDuringRegistration:YES
nextLink:nextLink
success:^
{
NSMutableDictionary *threepidCreds = [NSMutableDictionary dictionaryWithDictionary:@{
@"client_secret": self->submittedEmail.clientSecret,
@"sid": self->submittedEmail.sid
}];
if (identityServer)
{
NSURL *identServerURL = [NSURL URLWithString:identityServer];
threepidCreds[@"id_server"] = identServerURL.host;
}
NSURL *identServerURL = [NSURL URLWithString:restClient.identityServer];
NSDictionary *parameters;
parameters = @{
@"auth": @{@"session":currentSession.session, @"threepid_creds": @{@"client_secret": submittedEmail.clientSecret, @"id_server": identServerURL.host, @"sid": submittedEmail.sid}, @"type": kMXLoginFlowTypeEmailIdentity},
@"auth": @{
@"session":self->currentSession.session,
@"threepid_creds": threepidCreds,
@"type": kMXLoginFlowTypeEmailIdentity},
@"username": self.userLoginTextField.text,
@"password": self.passWordTextField.text,
@"bind_msisdn": @([self isFlowCompleted:kMXLoginFlowTypeMSISDN]),
@"bind_email": @(YES)
};
[self hideInputsContainer];
@ -687,6 +737,9 @@
callback(nil, error);
}];
} failure:^(NSError *error) {
callback(nil, error);
}];
// Async response
return;
@ -702,11 +755,13 @@
if (response.length)
{
NSDictionary *parameters = @{
@"auth": @{@"session":currentSession.session, @"response": response, @"type": kMXLoginFlowTypeRecaptcha},
@"auth": @{
@"session":currentSession.session,
@"response": response,
@"type": kMXLoginFlowTypeRecaptcha
},
@"username": self.userLoginTextField.text,
@"password": self.passWordTextField.text,
@"bind_msisdn": @([self isFlowCompleted:kMXLoginFlowTypeMSISDN]),
@"bind_email": @([self isFlowCompleted:kMXLoginFlowTypeEmailIdentity])
};
callback(parameters, nil);
@ -725,11 +780,12 @@
else if ([self isFlowSupported:kMXLoginFlowTypeDummy] && ![self isFlowCompleted:kMXLoginFlowTypeDummy])
{
parameters = @{
@"auth": @{@"session":currentSession.session, @"type": kMXLoginFlowTypeDummy},
@"auth": @{
@"session":currentSession.session,
@"type": kMXLoginFlowTypeDummy
},
@"username": self.userLoginTextField.text,
@"password": self.passWordTextField.text,
@"bind_msisdn": @(NO),
@"bind_email": @(NO)
};
}
else if ([self isFlowSupported:kMXLoginFlowTypePassword] && ![self isFlowCompleted:kMXLoginFlowTypePassword])
@ -758,9 +814,7 @@
@"type": kMXLoginFlowTypeTerms
},
@"username": self.userLoginTextField.text,
@"password": self.passWordTextField.text,
@"bind_msisdn": @([self isFlowCompleted:kMXLoginFlowTypeMSISDN]),
@"bind_email": @([self isFlowCompleted:kMXLoginFlowTypeEmailIdentity])
@"password": self.passWordTextField.text
};
callback(parameters, nil);
}];
@ -934,16 +988,31 @@
}
// Check validity of the required parameters
if (!homeserverURL.length || !identityURL.length || !clientSecret.length || !sid.length || !sessionId.length)
if (!homeserverURL.length || !clientSecret.length || !sid.length || !sessionId.length)
{
NSLog(@"[AuthInputsView] setExternalRegistrationParameters failed: wrong parameters");
return NO;
}
// Prepare the registration parameters (Ready to use)
NSMutableDictionary *threepidCreds = [NSMutableDictionary dictionaryWithDictionary:@{
@"client_secret": clientSecret,
@"sid": sid
}];
if (identityURL)
{
NSURL *identServerURL = [NSURL URLWithString:identityURL];
threepidCreds[@"id_server"] = identServerURL.host;
}
externalRegistrationParameters = @{
@"auth": @{@"session": sessionId, @"threepid_creds": @{@"client_secret": clientSecret, @"id_server": identServerURL.host, @"sid": sid}, @"type": kMXLoginFlowTypeEmailIdentity},
@"auth": @{
@"session": sessionId,
@"threepid_creds": threepidCreds,
@"type": kMXLoginFlowTypeEmailIdentity
},
};
// Hide all inputs by default
@ -1172,7 +1241,7 @@
self.emailContainer.hidden = NO;
self.messageLabel.hidden = NO;
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_email_message", @"Vector", nil);
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_email_message_2", @"Vector", nil);
lastViewContainer = self.emailContainer;
}
@ -1201,22 +1270,14 @@
self.emailTextField.returnKeyType = UIReturnKeyNext;
self.phoneContainerTopConstraint.constant = 50;
if (self.areAllThirdPartyIdentifiersRequired)
{
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_email_and_phone_message", @"Vector", nil);
}
else
{
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_email_phone_message", @"Vector", nil);
}
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_email_phone_message_2", @"Vector", nil);
}
else
{
self.phoneContainerTopConstraint.constant = 0;
self.messageLabel.hidden = NO;
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_phone_message", @"Vector", nil);
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_add_phone_message_2", @"Vector", nil);
}
lastViewContainer = self.phoneContainer;
@ -1606,7 +1667,7 @@
inputsAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"auth_msisdn_validation_title", @"Vector", nil) message:NSLocalizedStringFromTable(@"auth_msisdn_validation_message", @"Vector", nil) preferredStyle:UIAlertControllerStyleAlert];
[inputsAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"abort"]
[inputsAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
@ -1646,25 +1707,40 @@
{
[self->submittedMSISDN submitValidationToken:smsCode success:^{
// Retrieve the REST client from delegate
MXRestClient *restClient;
// Retrieve the identity service from delegate
MXIdentityService *identityService;
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationRestClient:)])
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationIdentityService:)])
{
restClient = [self.delegate authInputsViewThirdPartyIdValidationRestClient:self];
identityService = [self.delegate authInputsViewThirdPartyIdValidationIdentityService:self];
}
NSURL *identServerURL = [NSURL URLWithString:restClient.identityServer];
NSString *identityServer = identityService.identityServer;
if (identityServer)
{
NSURL *identServerURL = [NSURL URLWithString:identityServer];
NSDictionary *parameters;
parameters = @{
@"auth": @{@"session":self->currentSession.session, @"threepid_creds": @{@"client_secret": self->submittedMSISDN.clientSecret, @"id_server": identServerURL.host, @"sid": self->submittedMSISDN.sid}, @"type": kMXLoginFlowTypeMSISDN},
@"auth": @{
@"session":self->currentSession.session,
@"threepid_creds": @{
@"client_secret": self->submittedMSISDN.clientSecret,
@"id_server": identServerURL.host,
@"sid": self->submittedMSISDN.sid
},
@"type": kMXLoginFlowTypeMSISDN
},
@"username": self.userLoginTextField.text,
@"password": self.passWordTextField.text,
@"bind_msisdn": @(YES),
@"bind_email": @([self isFlowCompleted:kMXLoginFlowTypeEmailIdentity])
@"password": self.passWordTextField.text
};
callback(parameters, nil);
}
else
{
NSLog(@"[AuthInputsView] Failed to retrieve identity server URL");
}
} failure:^(NSError *error) {
@ -1790,4 +1866,16 @@
return NO;
}
- (void)checkIdentityServerRequirement:(MXRestClient*)mxRestClient
success:(void (^)(BOOL identityServerRequired))success
failure:(void (^)(NSError *error))failure
{
[mxRestClient supportedMatrixVersions:^(MXMatrixVersions *matrixVersions) {
NSLog(@"[AuthInputsView] checkIdentityServerRequirement: %@", matrixVersions.doesServerRequireIdentityServerParam ? @"YES": @"NO");
success(matrixVersions.doesServerRequireIdentityServerParam);
} failure:failure];
}
@end

View file

@ -237,6 +237,8 @@
if (restClient)
{
[self checkIdentityServerRequirement:restClient success:^{
// Launch email validation
NSString *clientSecret = [MXTools generateSecret];
@ -250,14 +252,19 @@
if (strongSelf) {
strongSelf.didPrepareParametersCallback = callback;
NSMutableDictionary *threepidCreds = [NSMutableDictionary dictionaryWithDictionary:@{
@"client_secret": clientSecret,
@"sid": sid
}];
if (restClient.identityServer)
{
NSURL *identServerURL = [NSURL URLWithString:restClient.identityServer];
threepidCreds[@"id_server"] = identServerURL.host;
}
strongSelf.parameters = @{
@"auth": @{
@"threepid_creds": @{
@"client_secret": clientSecret,
@"id_server": identServerURL.host,
@"sid": sid
},
@"threepid_creds": threepidCreds,
@"type": kMXLoginFlowTypeEmailIdentity
},
@"new_password": strongSelf.passWordTextField.text
@ -329,6 +336,9 @@
[self.delegate authInputsView:self presentAlertController:self->inputsAlert];
}
}];
} failure:^(NSError *error) {
callback(nil, error);
}];
// Async response
return;
@ -400,6 +410,29 @@
[self layoutIfNeeded];
}
- (void)checkIdentityServerRequirement:(MXRestClient*)mxRestClient success:(void (^)(void))success failure:(void (^)(NSError *))failure
{
[mxRestClient supportedMatrixVersions:^(MXMatrixVersions *matrixVersions) {
NSLog(@"[ForgotPasswordInputsView] checkIdentityServerRequirement: %@", matrixVersions.doesServerRequireIdentityServerParam ? @"YES": @"NO");
if (matrixVersions.doesServerRequireIdentityServerParam
&& !mxRestClient.identityServer)
{
failure([NSError errorWithDomain:MXKAuthErrorDomain
code:0
userInfo:@{
NSLocalizedDescriptionKey:[NSBundle mxk_localizedStringForKey:@"auth_reset_password_error_is_required"]
}]);
}
else
{
success();
}
} failure:failure];
}
#pragma mark - actions
- (void)didCheckEmail:(id)sender

View file

@ -26,4 +26,7 @@
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *callerImageViewWidthConstraint;
// At the end of call, this flag indicates if the prompt to use the fallback should be displayed
@property (nonatomic) BOOL shouldPromptForStunServerFallback;
@end

View file

@ -39,6 +39,9 @@
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
// Flag to compute self.shouldPromptForStunServerFallback
BOOL promptForStunServerFallback;
}
@end
@ -52,6 +55,9 @@
// Setup `MXKViewControllerHandling` properties
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
promptForStunServerFallback = NO;
_shouldPromptForStunServerFallback = NO;
}
- (void)viewDidLoad
@ -229,6 +235,13 @@
#pragma mark - MXCallDelegate
- (void)call:(MXCall *)call stateDidChange:(MXCallState)state reason:(MXEvent *)event
{
[super call:call stateDidChange:state reason:event];
[self checkStunServerFallbackWithCallState:state];
}
- (void)call:(MXCall *)call didEncounterError:(NSError *)error
{
if ([error.domain isEqualToString:MXEncryptingErrorDomain]
@ -333,6 +346,41 @@
}
}
#pragma mark - Fallback STUN server
- (void)checkStunServerFallbackWithCallState:(MXCallState)callState
{
// Detect if we should display the prompt to fallback to the STUN server defined
// in the app plist if the homeserver does not provide STUN or TURN servers.
// We should if the call ends while we were in connecting state
if (!self.mainSession.callManager.turnServers
&& !self.mainSession.callManager.fallbackSTUNServer
&& !RiotSettings.shared.isAllowStunServerFallbackHasBeenSetOnce)
{
switch (callState)
{
case MXCallStateConnecting:
promptForStunServerFallback = YES;
break;
case MXCallStateConnected:
promptForStunServerFallback = NO;
break;
case MXCallStateEnded:
if (promptForStunServerFallback)
{
_shouldPromptForStunServerFallback = YES;
}
default:
break;
}
}
}
#pragma mark - Properties
- (UIImage*)picturePlaceholder

View file

@ -34,9 +34,20 @@ final class ActivityIndicatorPresenter: ActivityIndicatorPresenterType {
private weak var activityIndicatorView: ActivityIndicatorView?
private weak var presentingView: UIView?
var isPresenting: Bool {
return self.activityIndicatorView != nil
}
// MARK: - Public
func presentActivityIndicator(on view: UIView, animated: Bool, completion: (() -> Void)? = nil) {
if self.presentingView != nil {
if let completion = completion {
completion()
}
return
}
self.presentingView = view
view.isUserInteractionEnabled = false
@ -85,6 +96,7 @@ final class ActivityIndicatorPresenter: ActivityIndicatorPresenterType {
}
presentingView.isUserInteractionEnabled = true
self.presentingView = nil
let animationInstructions = {
activityIndicatorView.alpha = 0

View file

@ -149,8 +149,10 @@
// Screen tracking
[[Analytics sharedInstance] trackScreen:_screenName];
// Check whether the access to the local contacts has not been already asked.
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined)
// Check whether the access to the local contacts has not been already asked
// and check that the user has decided to use or not to use an identity server
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined
|| !contactsDataSource.mxSession.hasAccountDataIdentityServerValue)
{
// Allow by default the local contacts sync in order to discover matrix users.
// This setting change will trigger the loading of the local contacts, which will automatically

View file

@ -86,13 +86,6 @@
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateLocalContactsNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification object:nil];
// Refresh the matrix identifiers for all the local contacts.
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] != CNAuthorizationStatusNotDetermined)
{
// Refresh the matrix identifiers for all the local contacts.
[[MXKContactManager sharedManager] updateMatrixIDsForAllLocalContacts];
}
}
return self;
}
@ -622,8 +615,16 @@
switch ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts])
{
case CNAuthorizationStatusAuthorized:
if (hideNonMatrixEnabledContacts && !self.mxSession.identityService)
{
// Because we cannot make lookups with no IS
tableViewCell.textLabel.text = NSLocalizedStringFromTable(@"contacts_address_book_no_identity_server", @"Vector", nil);
}
else
{
// Because there is no contacts on the device
tableViewCell.textLabel.text = NSLocalizedStringFromTable(@"contacts_address_book_no_contact", @"Vector", nil);
}
break;
case CNAuthorizationStatusNotDetermined:

View file

@ -1011,10 +1011,13 @@
// The identity server must be defined
if (!self.mainSession.matrixRestClient.identityServer)
{
MXError *error = [[MXError alloc] initWithErrorCode:kMXSDKErrCodeStringMissingParameters error:@"No supplied identity server URL"];
NSLog(@"[ContactDetailsViewController] Invite %@ failed", participantId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:[error createNSError]];
[self removePendingActionMask];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"error"]
message:NSLocalizedStringFromTable(@"room_participants_start_new_chat_error_using_user_email_without_identity_server", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
return;
}

View file

@ -26,7 +26,7 @@ NSString *const kIntegrationManagerMainScreen = nil;
NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
@interface IntegrationManagerViewController ()
@interface IntegrationManagerViewController () <ServiceTermsModalCoordinatorBridgePresenterDelegate>
{
MXSession *mxSession;
NSString *roomId;
@ -37,6 +37,8 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
MXHTTPOperation *operation;
}
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
@end
@implementation IntegrationManagerViewController
@ -67,10 +69,15 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
operation = nil;
}
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidLoad
{
[super viewWillAppear:animated];
[super viewDidLoad];
[self loadData];
}
- (void)loadData
{
if (!self.URL && !operation)
{
[self startActivityIndicator];
@ -94,9 +101,17 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
self->operation = nil;
[self stopActivityIndicator];
if ([error.domain isEqualToString:WidgetManagerErrorDomain]
&& error.code == WidgetManagerErrorCodeTermsNotSigned)
{
[self presentTerms];
}
else
{
[self withdrawViewControllerAnimated:YES completion:^{
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
}
}
@ -681,4 +696,40 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
}];
}
#pragma mark - Service terms
- (void)presentTerms
{
WidgetManagerConfig *config = [[WidgetManager sharedManager] configForUser:mxSession.myUser.userId];
NSLog(@"[IntegrationManagerVC] presentTerms for %@", config.baseUrl);
ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter = [[ServiceTermsModalCoordinatorBridgePresenter alloc] initWithSession:mxSession baseUrl:config.baseUrl
serviceType:MXServiceTypeIntegrationManager
outOfContext:NO
accessToken:config.scalarToken];
serviceTermsModalCoordinatorBridgePresenter.delegate = self;
[serviceTermsModalCoordinatorBridgePresenter presentFrom:self animated:YES];
self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self loadData];
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self withdrawViewControllerAnimated:YES completion:nil];
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
@end

View file

@ -19,10 +19,13 @@
#import "AppDelegate.h"
#import "IntegrationManagerViewController.h"
#import "Riot-Swift.h"
NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse('%@', %@);";
@interface WidgetViewController ()
@interface WidgetViewController () <ServiceTermsModalCoordinatorBridgePresenterDelegate>
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
@end
@ -363,15 +366,73 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
MXStrongifyAndReturnIfNil(self);
NSLog(@"[WidgetVC] fixScalarToken: DONE");
self.URL = [self stringByReplacingScalarTokenInString:self.URL byScalarToken:scalarToken];
self->webView.hidden = NO;
[self loadDataWithScalarToken:scalarToken];
} failure:^(NSError *error) {
NSLog(@"[WidgetVC] fixScalarToken: Error: %@", error);
if ([error.domain isEqualToString:WidgetManagerErrorDomain]
&& error.code == WidgetManagerErrorCodeTermsNotSigned)
{
[self presentTerms];
}
else
{
[self showErrorAsAlert:error];
}
}];
}
- (void)loadDataWithScalarToken:(NSString*)scalarToken
{
self.URL = [self stringByReplacingScalarTokenInString:self.URL byScalarToken:scalarToken];
self->webView.hidden = NO;
}
#pragma mark - Service terms
- (void)presentTerms
{
if (self.serviceTermsModalCoordinatorBridgePresenter)
{
return;
}
WidgetManagerConfig *config = [[WidgetManager sharedManager] configForUser:widget.mxSession.myUser.userId];
NSLog(@"[WidgetVC] presentTerms for %@", config.baseUrl);
ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter = [[ServiceTermsModalCoordinatorBridgePresenter alloc] initWithSession:widget.mxSession baseUrl:config.baseUrl
serviceType:MXServiceTypeIntegrationManager
outOfContext:NO
accessToken:config.scalarToken];
serviceTermsModalCoordinatorBridgePresenter.delegate = self;
[serviceTermsModalCoordinatorBridgePresenter presentFrom:self animated:YES];
self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
MXWeakify(self);
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
MXStrongifyAndReturnIfNil(self);
WidgetManagerConfig *config = [[WidgetManager sharedManager] configForUser:self->widget.mxSession.myUser.userId];
[self loadDataWithScalarToken:config.scalarToken];
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self withdrawViewControllerAnimated:YES completion:nil];
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
@end

View file

@ -99,8 +99,10 @@
{
[super viewWillAppear:animated];
// Check whether the access to the local contacts has not been already asked.
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined)
// Check whether the access to the local contacts has not been already asked
// and check that the user has decided to use or not to use an identity server
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined
|| !contactsDataSource.mxSession.hasAccountDataIdentityServerValue)
{
// Allow by default the local contacts sync in order to discover matrix users.
// This setting change will trigger the loading of the local contacts, which will automatically
@ -108,6 +110,9 @@
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
}
// Refresh the local contacts list.
[[MXKContactManager sharedManager] refreshLocalContacts];
[AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_people", @"Vector", nil);
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorOrange;

View file

@ -798,7 +798,9 @@
- (void)addRoomThirdPartyInviteToParticipants:(MXRoomThirdPartyInvite*)roomThirdPartyInvite roomState:(MXRoomState*)roomState
{
// If the homeserver has converted the 3pid invite into a room member, do no show it
if (![roomState memberWithThirdPartyInviteToken:roomThirdPartyInvite.token])
// If the invite has been revoked (null display name), do not show it too.
if (![roomState memberWithThirdPartyInviteToken:roomThirdPartyInvite.token]
&& roomThirdPartyInvite.displayname)
{
Contact *contact = [[Contact alloc] initMatrixContactWithDisplayName:roomThirdPartyInvite.displayname andMatrixID:nil];
contact.isThirdPartyInvite = YES;
@ -1373,8 +1375,6 @@
if (section == participantsSection || section == invitedSection)
{
__weak typeof(self) weakSelf = self;
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
@ -1384,6 +1384,7 @@
if (section == participantsSection && userParticipant && (0 == row) && !currentSearchText.length)
{
// Leave ?
MXWeakify(self);
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_participants_leave_prompt_title", @"Vector", nil)
message:NSLocalizedStringFromTable(@"room_participants_leave_prompt_msg", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
@ -1392,11 +1393,8 @@
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}
}]];
@ -1404,25 +1402,25 @@
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom leave:^{
MXStrongifyAndReturnIfNil(self);
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Leave room %@ failed", self.mxRoom.roomId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}]];
@ -1464,6 +1462,7 @@
if (row < participants.count)
{
Contact *contact = participants[row];
MXWeakify(self);
if (contact.mxMember)
{
@ -1479,11 +1478,8 @@
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}
}]];
@ -1491,16 +1487,16 @@
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom kickUser:memberUserId
reason:nil
success:^{
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
[participants removeObjectAtIndex:row];
@ -1510,32 +1506,61 @@
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Kick %@ failed", memberUserId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}]];
}
else
else if (contact.mxThirdPartyInvite)
{
// This is a third-party invite, it could not be removed until the api exists
// This is a third-party invite
currentAlert = [UIAlertController alertControllerWithTitle:nil
message:NSLocalizedStringFromTable(@"room_participants_remove_third_party_invite_msg", @"Vector", nil)
message:NSLocalizedStringFromTable(@"room_participants_remove_third_party_invite_prompt_msg", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self addPendingActionMask];
MXWeakify(self);
[self.mxRoom sendStateEventOfType:kMXEventTypeStringRoomThirdPartyInvite
content:@{} stateKey:contact.mxThirdPartyInvite.token success:^(NSString *eventId) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
[participants removeObjectAtIndex:row];
// Refresh display
[self.tableView reloadData];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Revoke 3pid invite failed");
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}]];
}
@ -1646,8 +1671,18 @@
[self removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Invite be email %@ failed", participantId);
// Alert user
if ([error.domain isEqualToString:kMXRestClientErrorDomain]
&& error.code == MXRestClientErrorMissingIdentityServer)
{
NSString *message = [NSBundle mxk_localizedStringForKey:@"error_invite_3pid_with_no_identity_server"];
[[AppDelegate theDelegate] showAlertWithTitle:message message:nil];
}
else
{
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
}];
}
else //if ([MXTools isMatrixUserIdentifier:participantId])

View file

@ -5098,8 +5098,16 @@
NSLog(@"[RoomVC] Invite be email %@ failed", participantId);
// Alert user
if ([error.domain isEqualToString:kMXRestClientErrorDomain]
&& error.code == MXRestClientErrorMissingIdentityServer)
{
NSString *message = [NSBundle mxk_localizedStringForKey:@"error_invite_3pid_with_no_identity_server"];
[[AppDelegate theDelegate] showAlertWithTitle:message message:nil];
}
else
{
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
}];
}
else //if ([MXTools isMatrixUserIdentifier:participantId])

View file

@ -0,0 +1,77 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
import UIKit
final class ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenCoordinatorType {
// MARK: - Properties
// MARK: Private
private var serviceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelType
private let serviceTermsModalScreenViewController: ServiceTermsModalScreenViewController
// Must be used only internally
var childCoordinators: [Coordinator] = []
// MARK: Public
weak var delegate: ServiceTermsModalScreenCoordinatorDelegate?
// MARK: - Setup
init(serviceTerms: MXServiceTerms, outOfContext: Bool = false) {
let serviceTermsModalScreenViewModel = ServiceTermsModalScreenViewModel(serviceTerms: serviceTerms, outOfContext: outOfContext)
let serviceTermsModalScreenViewController = ServiceTermsModalScreenViewController.instantiate(with: serviceTermsModalScreenViewModel)
self.serviceTermsModalScreenViewModel = serviceTermsModalScreenViewModel
self.serviceTermsModalScreenViewController = serviceTermsModalScreenViewController
}
// MARK: - Public methods
func start() {
self.serviceTermsModalScreenViewModel.coordinatorDelegate = self
}
func toPresentable() -> UIViewController {
return self.serviceTermsModalScreenViewController
}
}
// MARK: - ServiceTermsModalScreenViewModelCoordinatorDelegate
extension ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenViewModelCoordinatorDelegate {
func serviceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType) {
self.delegate?.serviceTermsModalScreenCoordinatorDidAccept(self)
}
func serviceTermsModalScreenViewModel(_ coordinator: ServiceTermsModalScreenViewModelType, displayPolicy policy: MXLoginPolicyData) {
self.delegate?.serviceTermsModalScreenCoordinator(self, displayPolicy: policy)
}
func serviceTermsModalScreenViewModelDidDecline(_ viewModel: ServiceTermsModalScreenViewModelType) {
self.delegate?.serviceTermsModalScreenCoordinatorDidDecline(self)
}
func serviceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType) {
self.delegate?.serviceTermsModalScreenCoordinatorDidCancel(self)
}
}

View file

@ -0,0 +1,31 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol ServiceTermsModalScreenCoordinatorDelegate: class {
func serviceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType)
func serviceTermsModalScreenCoordinator(_ coordinator: ServiceTermsModalScreenCoordinatorType, displayPolicy policy: MXLoginPolicyData)
func serviceTermsModalScreenCoordinatorDidDecline(_ coordinator: ServiceTermsModalScreenCoordinatorType)
func serviceTermsModalScreenCoordinatorDidCancel(_ coordinator: ServiceTermsModalScreenCoordinatorType)
}
/// `ServiceTermsModalScreenCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
protocol ServiceTermsModalScreenCoordinatorType: Coordinator, Presentable {
var delegate: ServiceTermsModalScreenCoordinatorDelegate? { get }
}

View file

@ -0,0 +1,28 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// ServiceTermsModalScreenViewController view actions exposed to view model
enum ServiceTermsModalScreenViewAction {
case load
case display(MXLoginPolicyData)
case accept
case decline
case cancel
}

View file

@ -0,0 +1,129 @@
<?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="retina6_1" 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>
<!--Service Terms Modal Screen View Controller-->
<scene sceneID="mt5-wz-YKA">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="ServiceTermsModalScreenViewController" 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="414" height="896"/>
<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="44" width="414" height="852"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
<rect key="frame" x="0.0" y="0.0" width="414" height="852"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
<rect key="frame" x="0.0" y="0.0" width="414" height="852"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To continue you need to accept the Terms of this service." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="20" y="20" width="374" height="100"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="1bP-8m-xrd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="swq-xw-ItG">
<rect key="frame" x="20" y="128" width="374" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="dcx-ju-f0Q">
<rect key="frame" x="20" y="736" width="374" height="96"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="d2a-Dw-Pu5"/>
</constraints>
<state key="normal" title="Accept"/>
<connections>
<action selector="acceptButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QHr-gh-dAD">
<rect key="frame" x="0.0" y="52" width="374" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="y6d-Vg-5PP"/>
</constraints>
<state key="normal" title="Decline">
<color key="titleColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="declineButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="MTU-9k-8yo"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="bxI-mu-qng" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="1K2-u8-QsL"/>
<constraint firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" constant="20" symbolic="YES" id="41D-TP-69f"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="8" symbolic="YES" id="9Gg-Xb-o2W"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="20" symbolic="YES" id="W1m-x0-TyS"/>
<constraint firstItem="dcx-ju-f0Q" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="Wcx-d6-M14"/>
<constraint firstAttribute="trailing" secondItem="swq-xw-ItG" secondAttribute="trailing" constant="20" symbolic="YES" id="Y5v-Gg-xkM"/>
<constraint firstAttribute="bottom" secondItem="dcx-ju-f0Q" secondAttribute="bottom" constant="20" symbolic="YES" id="cpa-Lq-Mvs"/>
<constraint firstAttribute="trailing" secondItem="dcx-ju-f0Q" secondAttribute="trailing" constant="20" symbolic="YES" id="eGO-kM-neh"/>
<constraint firstAttribute="width" priority="750" constant="500" id="glD-Sz-73O"/>
<constraint firstItem="dcx-ju-f0Q" firstAttribute="top" secondItem="swq-xw-ItG" secondAttribute="bottom" constant="8" symbolic="YES" id="zYm-2k-kvi"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="ze1-Iw-v9U"/>
</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 firstItem="e7g-um-WO4" firstAttribute="height" secondItem="9U2-KL-ZVA" secondAttribute="height" priority="500" id="GP8-i4-Fqh"/>
<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" 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.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" 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="acceptButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
<outlet property="declineButton" destination="QHr-gh-dAD" id="QOS-rE-4SP"/>
<outlet property="messageLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
<outlet property="tableView" destination="swq-xw-ItG" id="Fwb-Sc-bec"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3198" y="-647"/>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,313 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
final class ServiceTermsModalScreenViewController: UIViewController {
// MARK: - Constants
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet private weak var messageLabel: UILabel!
@IBOutlet private weak var tableView: UITableView!
@IBOutlet private weak var acceptButton: UIButton!
@IBOutlet private weak var declineButton: UIButton!
// MARK: Private
private var viewModel: ServiceTermsModalScreenViewModelType!
private var theme: Theme!
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private var policies: [MXLoginPolicyData] = []
/// Policies checked by the end user
private var checkedPolicies: Set<Int> = []
// MARK: - Setup
class func instantiate(with viewModel: ServiceTermsModalScreenViewModelType) -> ServiceTermsModalScreenViewController {
let viewController = StoryboardScene.ServiceTermsModalScreenViewController.initialScene.instantiate()
viewController.viewModel = viewModel
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = VectorL10n.serviceTermsModalTitle
self.setupViews()
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
self.messageLabel.textColor = theme.textPrimaryColor
self.acceptButton.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.acceptButton)
theme.applyStyle(onButton: self.declineButton)
self.declineButton.setTitleColor(self.theme.warningColor, for: .normal)
self.refreshViews()
}
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.setupTableView()
self.scrollView.keyboardDismissMode = .interactive
self.messageLabel.text = VectorL10n.serviceTermsModalMessage(self.viewModel.serviceUrl)
self.acceptButton.setTitle(VectorL10n.serviceTermsModalAcceptButton, for: .normal)
self.acceptButton.setTitle(VectorL10n.serviceTermsModalAcceptButton, for: .highlighted)
self.refreshAcceptButton()
if self.viewModel.outOfContext
&& self.viewModel.serviceType == MXServiceTypeIdentityService {
self.title = VectorL10n.serviceTermsModalTitleIdentityServer
self.messageLabel.text = VectorL10n.serviceTermsModalMessageIdentityServer(self.viewModel.serviceUrl)
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .normal)
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .highlighted)
} else {
self.declineButton.isHidden = true
}
}
private func setupTableView() {
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.separatorStyle = .none
self.tableView.alwaysBounceVertical = false
self.tableView.backgroundColor = .clear
self.tableView.register(TableViewCellWithCheckBoxAndLabel.nib(), forCellReuseIdentifier: TableViewCellWithCheckBoxAndLabel.defaultReuseIdentifier())
}
private func render(viewState: ServiceTermsModalScreenViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded(let policies, let alreadyAcceptedPoliciesUrls):
self.renderLoaded(policies: policies, alreadyAcceptedPoliciesUrls: alreadyAcceptedPoliciesUrls)
case .accepted:
self.renderAccepted()
case .error(let error):
self.render(error: error)
}
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderLoaded(policies: [MXLoginPolicyData], alreadyAcceptedPoliciesUrls: [String]) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.policies = policies
self.updateCheckedPolicies(with: alreadyAcceptedPoliciesUrls)
self.refreshViews()
}
private func renderAccepting() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderAccepted() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
private func refreshViews() {
self.tableView.reloadData()
self.refreshAcceptButton()
}
private func refreshAcceptButton() {
// Enable the button only if the user has accepted all policies
self.acceptButton.isEnabled = (self.policies.count == self.checkedPolicies.count)
}
// Pre-check policies already accepted by the user
private func updateCheckedPolicies(with acceptedPoliciesUrls: [String]) {
for url in acceptedPoliciesUrls {
if let policyIndex = self.policies.firstIndex(where: { $0.url == url }) {
checkedPolicies.insert(policyIndex)
}
}
}
// MARK: - Actions
@IBAction private func acceptButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .accept)
}
@IBAction private func declineButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .decline)
}
private func cancelButtonAction() {
self.viewModel.process(viewAction: .cancel)
}
@objc private func didTapCheckbox(sender: UITapGestureRecognizer) {
guard let policyIndex = sender.view?.tag else {
return
}
if self.checkedPolicies.contains(policyIndex) {
self.checkedPolicies.remove(policyIndex)
} else {
checkedPolicies.insert(policyIndex)
}
self.refreshViews()
}
}
// MARK: - ServiceTermsModalScreenViewModelViewDelegate
extension ServiceTermsModalScreenViewController: ServiceTermsModalScreenViewModelViewDelegate {
func serviceTermsModalScreenViewModel(_ viewModel: ServiceTermsModalScreenViewModelType, didUpdateViewState viewSate: ServiceTermsModalScreenViewState) {
self.render(viewState: viewSate)
}
}
// MARK: - UITableViewDataSource
extension ServiceTermsModalScreenViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.policies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCellWithCheckBoxAndLabel.defaultReuseIdentifier(), for: indexPath) as? TableViewCellWithCheckBoxAndLabel else {
fatalError("\(String(describing: TableViewCellWithCheckBoxAndLabel.self)) should be registered")
}
let policy = policies[indexPath.row]
let checked = checkedPolicies.contains(indexPath.row)
cell.label.attributedText = self.cellLabel(for: policy)
cell.label.font = .systemFont(ofSize: 15)
cell.isEnabled = checked
cell.accessoryType = .disclosureIndicator
cell.backgroundColor = self.theme.backgroundColor
if let checkBox = cell.checkBox, checkBox.gestureRecognizers?.isEmpty ?? true {
let gesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapCheckbox))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
checkBox.isUserInteractionEnabled = true
checkBox.tag = indexPath.row
checkBox.addGestureRecognizer(gesture)
}
return cell
}
func cellLabel(for policy: MXLoginPolicyData) -> NSAttributedString {
// TableViewCellWithCheckBoxAndLabel does not have a detailTextLabel
// Do it by hand
var labelDetail: String = ""
switch self.viewModel.serviceType {
case MXServiceTypeIdentityService:
labelDetail = VectorL10n.serviceTermsModalDescriptionForIdentityServer1
+ "\n"
+ VectorL10n.serviceTermsModalDescriptionForIdentityServer2
case MXServiceTypeIntegrationManager:
labelDetail = VectorL10n.serviceTermsModalDescriptionForIntegrationManager
default: break
}
let label = NSMutableAttributedString(string: policy.name,
attributes: [.foregroundColor: theme.textPrimaryColor])
label.append(NSAttributedString(string: "\n"))
label.append(NSAttributedString(string: labelDetail,
attributes: [.foregroundColor: theme.textSecondaryColor]))
return label
}
}
extension ServiceTermsModalScreenViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let policy = policies[indexPath.row]
self.viewModel.process(viewAction: .display(policy))
}
}

View file

@ -0,0 +1,135 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
final class ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelType {
// MARK: - Properties
let outOfContext: Bool
// MARK: Private
private let serviceTerms: MXServiceTerms
// MARK: Public
var serviceUrl: String {
return serviceTerms.baseUrl
}
var serviceType: MXServiceType {
return serviceTerms.serviceType
}
var policies: [MXLoginPolicyData]?
var alreadyAcceptedPoliciesUrls: [String] = []
weak var viewDelegate: ServiceTermsModalScreenViewModelViewDelegate?
weak var coordinatorDelegate: ServiceTermsModalScreenViewModelCoordinatorDelegate?
// MARK: - Setup
init(serviceTerms: MXServiceTerms, outOfContext: Bool) {
self.serviceTerms = serviceTerms
self.outOfContext = outOfContext
}
deinit {
}
// MARK: - Public
func process(viewAction: ServiceTermsModalScreenViewAction) {
switch viewAction {
case .load:
self.loadTerms()
case .display(let policy):
self.coordinatorDelegate?.serviceTermsModalScreenViewModel(self, displayPolicy: policy)
case .accept:
self.acceptTerms()
case .decline:
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidDecline(self)
case .cancel:
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidCancel(self)
}
}
// MARK: - Private
private func loadTerms() {
self.update(viewState: .loading)
self.serviceTerms.terms({ [weak self] (terms, alreadyAcceptedTermsUrls) in
guard let self = self else {
return
}
let policies = self.processTerms(terms: terms)
self.policies = policies
self.alreadyAcceptedPoliciesUrls = alreadyAcceptedTermsUrls ?? []
self.update(viewState: .loaded(policies: policies, alreadyAcceptedPoliciesUrls: self.alreadyAcceptedPoliciesUrls))
}, failure: { [weak self] error in
guard let self = self else {
return
}
self.update(viewState: .error(error))
})
}
private func acceptTerms() {
self.update(viewState: .loading)
self.serviceTerms.agree(toTerms: self.termsUrls, success: { [weak self] in
guard let self = self else {
return
}
self.update(viewState: .accepted)
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidAccept(self)
}, failure: { [weak self] (error) in
guard let self = self else {
return
}
self.update(viewState: .error(error))
})
}
private func processTerms(terms: MXLoginTerms?) -> [MXLoginPolicyData] {
if let policies = terms?.policiesData(forLanguage: Bundle.mxk_language(), defaultLanguage: Bundle.mxk_fallbackLanguage()) {
return policies
} else {
print("[ServiceTermsModalScreenViewModel] processTerms: Error: No terms for \(String(describing: terms))")
return []
}
}
private var termsUrls: [String] {
guard let policies = self.policies else {
return []
}
return policies.map({ return $0.url })
}
private func update(viewState: ServiceTermsModalScreenViewState) {
self.viewDelegate?.serviceTermsModalScreenViewModel(self, didUpdateViewState: viewState)
}
}

View file

@ -0,0 +1,47 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol ServiceTermsModalScreenViewModelViewDelegate: class {
func serviceTermsModalScreenViewModel(_ viewModel: ServiceTermsModalScreenViewModelType, didUpdateViewState viewSate: ServiceTermsModalScreenViewState)
}
protocol ServiceTermsModalScreenViewModelCoordinatorDelegate: class {
func serviceTermsModalScreenViewModel(_ coordinator: ServiceTermsModalScreenViewModelType, displayPolicy policy: MXLoginPolicyData)
func serviceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType)
func serviceTermsModalScreenViewModelDidDecline(_ viewModel: ServiceTermsModalScreenViewModelType)
func serviceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType)
}
/// Protocol describing the view model used by `ServiceTermsModalScreenViewController`
protocol ServiceTermsModalScreenViewModelType {
var serviceUrl: String { get }
var serviceType: MXServiceType { get }
/// If true, terms are displayed out of a context of a flow (like a background 3pids lookup)
/// In this case, the wording needs to provide more information about the intent
var outOfContext: Bool { get }
var policies: [MXLoginPolicyData]? { get set }
var alreadyAcceptedPoliciesUrls: [String] { get set }
var viewDelegate: ServiceTermsModalScreenViewModelViewDelegate? { get set }
var coordinatorDelegate: ServiceTermsModalScreenViewModelCoordinatorDelegate? { get set }
func process(viewAction: ServiceTermsModalScreenViewAction)
}

View file

@ -0,0 +1,27 @@
// File created from ScreenTemplate
// $ createScreen.sh Modal/Show ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// ServiceTermsModalScreenViewController view state
enum ServiceTermsModalScreenViewState {
case loading
case loaded(policies: [MXLoginPolicyData], alreadyAcceptedPoliciesUrls: [String])
case accepted
case error(Error)
}

View file

@ -0,0 +1,110 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
@objcMembers
final class ServiceTermsModalCoordinator: ServiceTermsModalCoordinatorType {
// MARK: - Properties
// MARK: Private
private let navigationRouter: NavigationRouterType
private let session: MXSession
private let serviceTerms: MXServiceTerms
private let outOfContext: Bool
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: ServiceTermsModalCoordinatorDelegate?
// MARK: - Setup
init(session: MXSession, baseUrl: String, serviceType: MXServiceType, outOfContext: Bool, accessToken: String) {
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
self.session = session
self.serviceTerms = MXServiceTerms(baseUrl: baseUrl, serviceType: serviceType, matrixSession: session, accessToken: accessToken)
self.outOfContext = outOfContext
}
// MARK: - Public methods
func start() {
let rootCoordinator = self.createServiceTermsModalLoadTermsScreenCoordinator()
rootCoordinator.start()
self.add(childCoordinator: rootCoordinator)
self.navigationRouter.setRootModule(rootCoordinator)
}
func toPresentable() -> UIViewController {
return self.navigationRouter.toPresentable()
}
// MARK: - Private methods
private func createServiceTermsModalLoadTermsScreenCoordinator() -> ServiceTermsModalScreenCoordinator {
let coordinator = ServiceTermsModalScreenCoordinator(serviceTerms: self.serviceTerms, outOfContext: self.outOfContext)
coordinator.delegate = self
return coordinator
}
private func showPolicy(policy: MXLoginPolicyData) {
// Display the policy webpage into our webview
let webViewViewController: WebViewViewController = WebViewViewController(url: policy.url)
webViewViewController.title = policy.name
let leftBarButtonItem: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "back_icon"), style: .plain, target: self, action: #selector(didTapCancelOnPolicyScreen))
webViewViewController.navigationItem.leftBarButtonItem = leftBarButtonItem
self.navigationRouter.push(webViewViewController, animated: true, popCompletion: nil)
}
private func removePolicyScreen() {
self.navigationRouter.popModule(animated: true)
}
@objc private func didTapCancelOnPolicyScreen() {
self.removePolicyScreen()
}
}
// MARK: - ServiceTermsModalLoadTermsScreenCoordinatorDelegate
extension ServiceTermsModalCoordinator: ServiceTermsModalScreenCoordinatorDelegate {
func serviceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorDidAccept(self)
}
func serviceTermsModalScreenCoordinator(_ coordinator: ServiceTermsModalScreenCoordinatorType, displayPolicy policy: MXLoginPolicyData) {
self.showPolicy(policy: policy)
}
func serviceTermsModalScreenCoordinatorDidDecline(_ coordinator: ServiceTermsModalScreenCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorDidDecline(self)
}
func serviceTermsModalScreenCoordinatorDidCancel(_ coordinator: ServiceTermsModalScreenCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorDidCancel(self)
}
}

View file

@ -0,0 +1,110 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalLoadTermsScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
@objc protocol ServiceTermsModalCoordinatorBridgePresenterDelegate {
func serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter)
func serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter, session: MXSession)
func serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter)
}
/// ServiceTermsModalCoordinatorBridgePresenter enables to start ServiceTermsModalCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
@objcMembers
final class ServiceTermsModalCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let baseUrl: String
private let serviceType: MXServiceType
private let outOfContext: Bool
private let accessToken: String
private var coordinator: ServiceTermsModalCoordinator?
// MARK: Public
weak var delegate: ServiceTermsModalCoordinatorBridgePresenterDelegate?
var isPresenting: Bool {
return self.coordinator != nil
}
// MARK: - Setup
init(session: MXSession, baseUrl: String, serviceType: MXServiceType, outOfContext: Bool = false, accessToken: String) {
self.session = session
self.baseUrl = baseUrl
self.serviceType = serviceType
self.outOfContext = outOfContext
self.accessToken = accessToken
super.init()
}
// MARK: - Public
// NOTE: Default value feature is not compatible with Objective-C.
// func present(from viewController: UIViewController, animated: Bool) {
// self.present(from: viewController, animated: animated)
// }
func present(from viewController: UIViewController, animated: Bool) {
let serviceTermsModalCoordinator = ServiceTermsModalCoordinator(session: self.session, baseUrl: self.baseUrl, serviceType: self.serviceType, outOfContext: self.outOfContext, accessToken: accessToken)
serviceTermsModalCoordinator.delegate = self
viewController.present(serviceTermsModalCoordinator.toPresentable(), animated: animated, completion: nil)
serviceTermsModalCoordinator.start()
if let coordinator = self.coordinator {
coordinator.toPresentable().dismiss(animated: false, completion: nil)
}
self.coordinator = serviceTermsModalCoordinator
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
guard let coordinator = self.coordinator else {
return
}
coordinator.toPresentable().dismiss(animated: animated) {
self.coordinator = nil
if let completion = completion {
completion()
}
}
}
}
// MARK: - ServiceTermsModalCoordinatorDelegate
extension ServiceTermsModalCoordinatorBridgePresenter: ServiceTermsModalCoordinatorDelegate {
func serviceTermsModalCoordinatorDidAccept(_ coordinator: ServiceTermsModalCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept(self)
}
func serviceTermsModalCoordinatorDidDecline(_ coordinator: ServiceTermsModalCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline(self, session: self.session)
}
func serviceTermsModalCoordinatorDidCancel(_ coordinator: ServiceTermsModalCoordinatorType) {
self.delegate?.serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel(self)
}
}

View file

@ -0,0 +1,30 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Modal ServiceTermsModal ServiceTermsModalLoadTermsScreen
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol ServiceTermsModalCoordinatorDelegate: class {
func serviceTermsModalCoordinatorDidAccept(_ coordinator: ServiceTermsModalCoordinatorType)
func serviceTermsModalCoordinatorDidDecline(_ coordinator: ServiceTermsModalCoordinatorType)
func serviceTermsModalCoordinatorDidCancel(_ coordinator: ServiceTermsModalCoordinatorType)
}
/// `ServiceTermsModalCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
protocol ServiceTermsModalCoordinatorType: Coordinator, Presentable {
var delegate: ServiceTermsModalCoordinatorDelegate? { get }
}

View file

@ -0,0 +1,257 @@
/*
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 SettingsDiscoveryTableViewSectionDelegate: class {
func settingsDiscoveryTableViewSection(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection, tableViewCellClass: MXKTableViewCell.Type, forRow: Int) -> MXKTableViewCell
func settingsDiscoveryTableViewSectionDidUpdate(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection)
}
private enum DiscoverySectionRows {
case info(text: String)
case attributedInfo(attributedText: NSAttributedString)
case button(title: String, action: () -> Void)
case threePid(threePid: MX3PID)
}
@objc final class SettingsDiscoveryTableViewSection: NSObject, Themable {
// MARK: - Constants
private enum Constants {
static let defaultFont = UIFont.systemFont(ofSize: 17.0)
}
// MARK: - Properties
@objc weak var delegate: SettingsDiscoveryTableViewSectionDelegate?
// MARK: Private
private var theme: Theme!
private var viewModel: SettingsDiscoveryViewModel
// Need to know the state to make `cellForRow` deliver cells accordingly
private var viewState: SettingsDiscoveryViewState = .loading {
didSet {
self.updateRows()
}
}
private var discoveryRows: [DiscoverySectionRows] = []
// MARK: - Setup
@objc init(viewModel: SettingsDiscoveryViewModel) {
self.theme = ThemeService.shared().theme
self.viewModel = viewModel
super.init()
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
self.registerThemeServiceDidChangeThemeNotification()
}
// MARK: - Public
@objc func numberOfRows() -> Int {
return self.discoveryRows.count
}
@objc func cellForRow(atRow row: Int) -> UITableViewCell {
let discoveryRow = self.discoveryRows[row]
var cell: UITableViewCell?
let enableInteraction: Bool
if case .loading = self.viewState {
enableInteraction = false
} else {
enableInteraction = true
}
switch discoveryRow {
case .info(let infoText):
if let infoCell: MXKTableViewCell = self.cellType(at: row) {
infoCell.textLabel?.numberOfLines = 0
infoCell.textLabel?.text = infoText
infoCell.selectionStyle = .none
cell = infoCell
}
case .attributedInfo(attributedText: let infoText):
if let infoCell: MXKTableViewCell = self.cellType(at: row) {
infoCell.textLabel?.numberOfLines = 0
infoCell.textLabel?.attributedText = infoText
infoCell.selectionStyle = .none
cell = infoCell
}
case .button(title: let title, action: let action):
if let buttonCell: MXKTableViewCellWithButton = self.cellType(at: row) {
buttonCell.mxkButton.setTitle(title, for: .normal)
buttonCell.mxkButton.setTitle(title, for: .highlighted)
buttonCell.mxkButton.vc_addAction(action: action)
buttonCell.mxkButton.isEnabled = enableInteraction
cell = buttonCell
}
case .threePid(let threePid):
if let detailCell: MXKTableViewCell = self.cellType(at: row) {
detailCell.accessoryType = .disclosureIndicator
let formattedThreePid: String?
switch threePid.medium {
case .email:
formattedThreePid = threePid.address
case .msisdn:
formattedThreePid = MXKTools.readableMSISDN(threePid.address)
default:
formattedThreePid = nil
}
detailCell.textLabel?.text = formattedThreePid
detailCell.isUserInteractionEnabled = enableInteraction
cell = detailCell
}
}
return cell ?? UITableViewCell()
}
@objc func reload() {
self.viewModel.process(viewAction: .load)
}
@objc func selectRow(_ row: Int) {
let discoveryRow = self.discoveryRows[row]
switch discoveryRow {
case .threePid(threePid: let threePid):
self.viewModel.process(viewAction: .select(threePid: threePid))
case .attributedInfo(attributedText: _):
if case let .loaded(displayMode) = self.viewState {
switch displayMode {
case .noThreePidsAdded, .threePidsAdded:
self.viewModel.process(viewAction: .tapUserSettingsLink)
default:
break
}
}
default:
break
}
}
func update(theme: Theme) {
self.theme = theme
self.updateRows()
}
// MARK: - Private
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func cellType<T: MXKTableViewCell>(at row: Int) -> T? {
let klass: T.Type = T.self
let tableViewCell = delegate?.settingsDiscoveryTableViewSection(self, tableViewCellClass: klass, forRow: row)
return tableViewCell as? T
}
private func updateRows() {
let discoveryRows: [DiscoverySectionRows]
switch self.viewState {
case .loading:
discoveryRows = self.discoveryRows
case .loaded(let displayMode):
switch displayMode {
case .noIdentityServer:
discoveryRows = [
.info(text: VectorL10n.settingsDiscoveryNoIdentityServer)
]
case .termsNotSigned(let host):
discoveryRows = [
.info(text: VectorL10n.settingsDiscoveryTermsNotSigned(host)),
.button(title: VectorL10n.accept, action: { [weak self] in
self?.viewModel.process(viewAction: .acceptTerms)
})
]
case .noThreePidsAdded:
discoveryRows = [
.attributedInfo(attributedText: self.threePidsManagementInfoAttributedString())
]
case .threePidsAdded(let emails, let phoneNumbers):
let emailThreePids = emails.map { (email) -> DiscoverySectionRows in
return .threePid(threePid: email)
}
let phoneNumbersThreePids = phoneNumbers.map { (phoneNumber) -> DiscoverySectionRows in
return .threePid(threePid: phoneNumber)
}
var threePidsRows: [DiscoverySectionRows] = []
threePidsRows.append(contentsOf: emailThreePids)
threePidsRows.append(contentsOf: phoneNumbersThreePids)
threePidsRows.append(.attributedInfo(attributedText: self.threePidsManagementInfoAttributedString()))
discoveryRows = threePidsRows
}
case .error:
discoveryRows = [
.info(text: VectorL10n.settingsDiscoveryErrorMessage),
.button(title: VectorL10n.retry, action: { [weak self] in
self?.viewModel.process(viewAction: .load)
})
]
}
self.discoveryRows = discoveryRows
}
private func threePidsManagementInfoAttributedString() -> NSAttributedString {
let attributedInfoString = NSMutableAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart1,
attributes: [.foregroundColor: self.theme.textPrimaryColor, .font: Constants.defaultFont])
attributedInfoString.append(NSAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart2,
attributes: [.foregroundColor: self.theme.tintColor, .font: Constants.defaultFont]))
attributedInfoString.append(NSAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart3,
attributes: [.foregroundColor: self.theme.tintColor, .font: Constants.defaultFont]))
return attributedInfoString
}
}
// MARK: - SettingsDiscoveryViewModelViewDelegate
extension SettingsDiscoveryTableViewSection: SettingsDiscoveryViewModelViewDelegate {
func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModelType, didUpdateViewState viewState: SettingsDiscoveryViewState) {
self.viewState = viewState
// The tableview datasource will call `self.cellForRow()`
self.delegate?.settingsDiscoveryTableViewSectionDidUpdate(self)
}
}

View file

@ -0,0 +1,25 @@
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// SettingsDiscoveryTableViewSection view actions exposed to view model
enum SettingsDiscoveryViewAction {
case load
case acceptTerms
case select(threePid: MX3PID)
case tapUserSettingsLink
}

View file

@ -0,0 +1,200 @@
/*
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 final class SettingsDiscoveryViewModel: NSObject, SettingsDiscoveryViewModelType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var identityService: MXIdentityService?
private var serviceTerms: MXServiceTerms?
private var viewState: SettingsDiscoveryViewState?
private var threePIDs: [MX3PID] = []
// MARK: Public
weak var viewDelegate: SettingsDiscoveryViewModelViewDelegate?
@objc weak var coordinatorDelegate: SettingsDiscoveryViewModelCoordinatorDelegate?
// MARK: - Setup
@objc init(session: MXSession, thirdPartyIdentifiers: [MXThirdPartyIdentifier]) {
self.session = session
let identityService = session.identityService
if let identityService = identityService {
self.serviceTerms = MXServiceTerms(baseUrl: identityService.identityServer, serviceType: MXServiceTypeIdentityService, matrixSession: session, accessToken: nil)
}
self.identityService = identityService
self.threePIDs = SettingsDiscoveryViewModel.threePids(from: thirdPartyIdentifiers)
super.init()
}
// MARK: - Public
func process(viewAction: SettingsDiscoveryViewAction) {
switch viewAction {
case .load:
self.checkTerms()
case .acceptTerms:
self.acceptTerms()
case .select(threePid: let threePid):
self.coordinatorDelegate?.settingsDiscoveryViewModel(self, didSelectThreePidWith: threePid.medium.identifier, and: threePid.address)
case .tapUserSettingsLink:
self.coordinatorDelegate?.settingsDiscoveryViewModelDidTapUserSettingsLink(self)
}
}
@objc func update(thirdPartyIdentifiers: [MXThirdPartyIdentifier]) {
self.threePIDs = SettingsDiscoveryViewModel.threePids(from: thirdPartyIdentifiers)
// Update view state only if three3pids was previously
guard let viewState = self.viewState,
case let .loaded(displayMode: displayMode) = viewState else {
return
}
let canDisplayThreePids: Bool
switch displayMode {
case .threePidsAdded, .noThreePidsAdded:
canDisplayThreePids = true
default:
canDisplayThreePids = false
}
if canDisplayThreePids {
self.updateViewStateFromCurrentThreePids()
}
}
// MARK: - Private
private func checkTerms() {
guard let identityService = self.identityService, let serviceTerms = self.serviceTerms else {
self.update(viewState: .loaded(displayMode: .noIdentityServer))
return
}
guard self.canCheckTerms() else {
return
}
self.update(viewState: .loading)
serviceTerms.areAllTermsAgreed({ (agreedTermsProgress) in
if agreedTermsProgress.isFinished || agreedTermsProgress.totalUnitCount == 0 {
// Display three pids if presents
self.updateViewStateFromCurrentThreePids()
} else {
let identityServer = identityService.identityServer
let identityServerHost = URL(string: identityServer)?.host ?? identityServer
self.update(viewState: .loaded(displayMode: .termsNotSigned(host: identityServerHost)))
}
}, failure: { (error) in
self.update(viewState: .error(error))
})
}
private func acceptTerms() {
guard let identityService = self.identityService else {
self.update(viewState: .loaded(displayMode: .noIdentityServer))
return
}
// Launch an identity server request to trigger terms modal apparition
identityService.account { (response) in
switch response {
case .success:
// Display three pids if presents
self.updateViewStateFromCurrentThreePids()
case .failure(let error):
if MXError(nsError: error)?.errcode == kMXErrCodeStringTermsNotSigned {
// Identity terms modal should appear
} else {
self.update(viewState: .error(error))
}
}
}
}
private func canCheckTerms() -> Bool {
guard let viewState = self.viewState else {
return true
}
let canCheckTerms: Bool
if case .loading = viewState {
canCheckTerms = false
} else {
canCheckTerms = true
}
return canCheckTerms
}
private func updateViewStateFromCurrentThreePids() {
self.updateViewState(with: self.threePIDs)
}
private func updateViewState(with threePids: [MX3PID]) {
let viewState: SettingsDiscoveryViewState
if threePids.isEmpty {
viewState = .loaded(displayMode: .noThreePidsAdded)
} else {
let emails = threePids.compactMap { (threePid) -> MX3PID? in
if case .email = threePid.medium {
return threePid
} else {
return nil
}
}
let phoneNumbers = threePids.compactMap { (threePid) -> MX3PID? in
if case .msisdn = threePid.medium {
return threePid
} else {
return nil
}
}
viewState = .loaded(displayMode: .threePidsAdded(emails: emails, phoneNumbers: phoneNumbers))
}
self.update(viewState: viewState)
}
private func update(viewState: SettingsDiscoveryViewState) {
self.viewState = viewState
self.viewDelegate?.settingsDiscoveryViewModel(self, didUpdateViewState: viewState)
}
private class func threePids(from thirdPartyIdentifiers: [MXThirdPartyIdentifier]) -> [MX3PID] {
return thirdPartyIdentifiers.map({ (thirdPartyIdentifier) -> MX3PID in
return MX3PID(medium: MX3PID.Medium(identifier: thirdPartyIdentifier.medium), address: thirdPartyIdentifier.address)
})
}
}

View file

@ -0,0 +1,35 @@
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol SettingsDiscoveryViewModelViewDelegate: class {
func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModelType, didUpdateViewState viewState: SettingsDiscoveryViewState)
}
@objc protocol SettingsDiscoveryViewModelCoordinatorDelegate: class {
func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModel, didSelectThreePidWith medium: String, and address: String)
func settingsDiscoveryViewModelDidTapUserSettingsLink(_ viewModel: SettingsDiscoveryViewModel)
}
protocol SettingsDiscoveryViewModelType {
var viewDelegate: SettingsDiscoveryViewModelViewDelegate? { get set }
var coordinatorDelegate: SettingsDiscoveryViewModelCoordinatorDelegate? { get set }
func process(viewAction: SettingsDiscoveryViewAction)
}

View file

@ -0,0 +1,37 @@
/*
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
/// SettingsDiscoveryTableViewSection view state
enum SettingsDiscoveryViewState {
case loading
case loaded(displayMode: SettingsDiscoveryDisplayMode)
case error(Error)
}
/// SettingsDiscoveryTableViewSection `loaded` view state dipslay modes
///
/// - noIdentityServer: No identity server configured.
/// - termsNotSigned: Identity server terms are not signed.
/// - noThreePidsAdded: No three pids added to the user HS account.
/// - threePidsAdded: Three pids added to the user HS account.
enum SettingsDiscoveryDisplayMode {
case noIdentityServer
case termsNotSigned(host: String)
case noThreePidsAdded
case threePidsAdded(emails: [MX3PID], phoneNumbers: [MX3PID])
}

View file

@ -0,0 +1,56 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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 SettingsDiscoveryThreePidDetailsCoordinator: SettingsDiscoveryThreePidDetailsCoordinatorType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var settingsDiscoveryThreePidDetailsViewModel: SettingsDiscoveryThreePidDetailsViewModelType
private let settingsDiscoveryThreePidDetailsViewController: SettingsDiscoveryThreePidDetailsViewController
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
// MARK: - Setup
init(session: MXSession, threePid: MX3PID) {
self.session = session
let settingsDiscoveryThreePidDetailsViewModel = SettingsDiscoveryThreePidDetailsViewModel(session: self.session, threePid: threePid)
let settingsDiscoveryThreePidDetailsViewController = SettingsDiscoveryThreePidDetailsViewController.instantiate(with: settingsDiscoveryThreePidDetailsViewModel)
self.settingsDiscoveryThreePidDetailsViewModel = settingsDiscoveryThreePidDetailsViewModel
self.settingsDiscoveryThreePidDetailsViewController = settingsDiscoveryThreePidDetailsViewController
}
// MARK: - Public methods
func start() {
}
func toPresentable() -> UIViewController {
return self.settingsDiscoveryThreePidDetailsViewController
}
}

View file

@ -0,0 +1,63 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Details SettingsDiscoveryThreePidDetails
/*
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
/// SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter enables to start SettingsDiscoveryThreePidDetailsCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
@objcMembers
final class SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let threePid: MX3PID
private var coordinator: SettingsDiscoveryThreePidDetailsCoordinator?
private var router: NavigationRouter?
// MARK: - Setup
init(session: MXSession, medium: String, adress: String) {
self.session = session
self.threePid = MX3PID(medium: MX3PID.Medium(identifier: medium), address: adress)
super.init()
}
// MARK: - Public
func push(from navigationController: UINavigationController, animated: Bool, popCompletion: (() -> Void)?) {
let router = NavigationRouter(navigationController: navigationController)
let settingsDiscoveryThreePidDetailsCoordinator = SettingsDiscoveryThreePidDetailsCoordinator(session: self.session, threePid: self.threePid)
router.push(settingsDiscoveryThreePidDetailsCoordinator, animated: animated) { [weak self] in
self?.coordinator = nil
self?.router = nil
popCompletion?()
}
settingsDiscoveryThreePidDetailsCoordinator.start()
self.coordinator = settingsDiscoveryThreePidDetailsCoordinator
self.router = router
}
}

View file

@ -0,0 +1,23 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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
/// `SettingsDiscoveryThreePidDetailsCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
protocol SettingsDiscoveryThreePidDetailsCoordinatorType: Coordinator, Presentable {
}

View file

@ -0,0 +1,29 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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
/// SettingsDiscoveryThreePidDetailsViewController view actions exposed to view model
enum SettingsDiscoveryThreePidDetailsViewAction {
case load
case share
case revoke
case cancelThreePidValidation
case confirmEmailValidation
case confirmMSISDNValidation(code: String)
}

View file

@ -0,0 +1,152 @@
<?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="ezK-Hg-0xv">
<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>
<!--Settings Discovery Three Pid Details View Controller-->
<scene sceneID="3dE-H2-gcP">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="ezK-Hg-0xv" customClass="SettingsDiscoveryThreePidDetailsViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="R2r-2G-yM5">
<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="q1k-fN-38j">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aP2-lr-60e">
<rect key="frame" x="0.0" y="0.0" width="375" height="277"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1h7-ek-3CO">
<rect key="frame" x="0.0" y="0.0" width="375" height="277"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="H6I-jF-SbB">
<rect key="frame" x="0.0" y="40" width="375" height="50"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="email" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vPl-rf-DWf">
<rect key="frame" x="20" y="16" width="36.5" 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="foo@matrix.org" textAlignment="right" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SP5-xI-3zJ">
<rect key="frame" x="66.5" y="16" width="288.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="SP5-xI-3zJ" firstAttribute="top" secondItem="vPl-rf-DWf" secondAttribute="top" id="5Mc-1w-3Qy"/>
<constraint firstAttribute="trailing" secondItem="SP5-xI-3zJ" secondAttribute="trailing" constant="20" id="9CG-58-qE1"/>
<constraint firstItem="vPl-rf-DWf" firstAttribute="leading" secondItem="H6I-jF-SbB" secondAttribute="leading" constant="20" id="Ngy-Un-QjA"/>
<constraint firstItem="vPl-rf-DWf" firstAttribute="top" secondItem="H6I-jF-SbB" secondAttribute="top" constant="16" id="eoq-0N-Ka3"/>
<constraint firstItem="SP5-xI-3zJ" firstAttribute="leading" secondItem="vPl-rf-DWf" secondAttribute="trailing" constant="10" id="gWa-Bh-BGZ"/>
<constraint firstAttribute="bottom" secondItem="vPl-rf-DWf" secondAttribute="bottom" constant="16" id="kmG-LA-aJL"/>
<constraint firstItem="SP5-xI-3zJ" firstAttribute="bottom" secondItem="vPl-rf-DWf" secondAttribute="bottom" id="tt6-jQ-Gtv"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PNH-63-Z3c">
<rect key="frame" x="0.0" y="115" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GnW-2q-pSU">
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="8qU-Gg-4dL"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
<state key="normal" title="Share">
<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="operationButtonAction:" destination="ezK-Hg-0xv" eventType="touchUpInside" id="uLq-yW-mh2"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="GnW-2q-pSU" firstAttribute="leading" secondItem="PNH-63-Z3c" secondAttribute="leading" id="45P-d7-TNc"/>
<constraint firstItem="GnW-2q-pSU" firstAttribute="top" secondItem="PNH-63-Z3c" secondAttribute="top" id="6w9-y9-Cic"/>
<constraint firstAttribute="bottom" secondItem="GnW-2q-pSU" secondAttribute="bottom" id="K0b-tM-nlb"/>
<constraint firstAttribute="trailing" secondItem="GnW-2q-pSU" secondAttribute="trailing" id="fvC-FK-JJE"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fYa-31-Xfd">
<rect key="frame" x="20" y="185" width="335" height="72"/>
<string key="text">Manage preferences for this email address, which other users can use to discover you and use to invite you to rooms. Add or remove email addresses in Accounts.</string>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="H6I-jF-SbB" secondAttribute="trailing" id="2eD-FF-4pI"/>
<constraint firstItem="fYa-31-Xfd" firstAttribute="top" secondItem="PNH-63-Z3c" secondAttribute="bottom" constant="20" id="7m2-AA-5BM"/>
<constraint firstItem="H6I-jF-SbB" firstAttribute="top" secondItem="1h7-ek-3CO" secondAttribute="top" constant="40" id="9nH-V1-qst"/>
<constraint firstItem="PNH-63-Z3c" firstAttribute="top" secondItem="H6I-jF-SbB" secondAttribute="bottom" constant="25" id="JJC-gO-Yl3"/>
<constraint firstAttribute="bottom" secondItem="fYa-31-Xfd" secondAttribute="bottom" constant="20" id="LMW-Qk-Dv4"/>
<constraint firstItem="fYa-31-Xfd" firstAttribute="leading" secondItem="1h7-ek-3CO" secondAttribute="leading" constant="20" id="PlF-wq-3sl"/>
<constraint firstItem="PNH-63-Z3c" firstAttribute="leading" secondItem="1h7-ek-3CO" secondAttribute="leading" id="alO-Yb-wVp"/>
<constraint firstAttribute="trailing" secondItem="PNH-63-Z3c" secondAttribute="trailing" id="brL-L8-ch1"/>
<constraint firstAttribute="width" priority="750" constant="500" id="l3L-bE-s7u"/>
<constraint firstItem="H6I-jF-SbB" firstAttribute="leading" secondItem="1h7-ek-3CO" secondAttribute="leading" id="nAN-0L-p44"/>
<constraint firstAttribute="trailing" secondItem="fYa-31-Xfd" secondAttribute="trailing" constant="20" id="pTH-Uc-hX7"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="1h7-ek-3CO" firstAttribute="centerX" secondItem="aP2-lr-60e" secondAttribute="centerX" id="CV4-kX-dzd"/>
<constraint firstItem="1h7-ek-3CO" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="aP2-lr-60e" secondAttribute="leading" id="F1Q-cV-TEV"/>
<constraint firstAttribute="bottom" secondItem="1h7-ek-3CO" secondAttribute="bottom" id="Hyc-7c-jZe"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1h7-ek-3CO" secondAttribute="trailing" id="MPu-qV-x2a"/>
<constraint firstItem="1h7-ek-3CO" firstAttribute="top" secondItem="aP2-lr-60e" secondAttribute="top" id="Ol3-Vk-aV2"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="aP2-lr-60e" firstAttribute="top" secondItem="q1k-fN-38j" secondAttribute="top" id="52d-aL-BvB"/>
<constraint firstAttribute="trailing" secondItem="aP2-lr-60e" secondAttribute="trailing" id="E3H-IS-rRu"/>
<constraint firstItem="aP2-lr-60e" firstAttribute="width" secondItem="q1k-fN-38j" secondAttribute="width" id="JlK-tw-JnA"/>
<constraint firstAttribute="bottom" secondItem="aP2-lr-60e" secondAttribute="bottom" id="dYG-fE-MEt"/>
<constraint firstItem="aP2-lr-60e" firstAttribute="leading" secondItem="q1k-fN-38j" secondAttribute="leading" id="whl-EE-953"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="juo-3g-nXq" firstAttribute="top" secondItem="q1k-fN-38j" secondAttribute="top" id="9hw-K8-RPD"/>
<constraint firstAttribute="bottom" secondItem="q1k-fN-38j" secondAttribute="bottom" id="Fad-dA-ZfQ"/>
<constraint firstItem="juo-3g-nXq" firstAttribute="trailing" secondItem="q1k-fN-38j" secondAttribute="trailing" id="JhW-Hh-xAm"/>
<constraint firstItem="q1k-fN-38j" firstAttribute="leading" secondItem="juo-3g-nXq" secondAttribute="leading" id="iYf-jl-9Jd"/>
</constraints>
<viewLayoutGuide key="safeArea" id="juo-3g-nXq"/>
</view>
<connections>
<outlet property="informationLabel" destination="fYa-31-Xfd" id="pgq-O8-lEO"/>
<outlet property="operationButton" destination="GnW-2q-pSU" id="7iB-Gx-ppD"/>
<outlet property="scrollView" destination="q1k-fN-38j" id="hkr-ze-2Ht"/>
<outlet property="threePidAdressLabel" destination="SP5-xI-3zJ" id="q5X-AN-eed"/>
<outlet property="threePidBackgroundView" destination="H6I-jF-SbB" id="s57-Tg-UUJ"/>
<outlet property="threePidTitleLabel" destination="vPl-rf-DWf" id="SMX-J1-71T"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Pf0-Pr-ZvT" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3772" y="-774"/>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,303 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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 SettingsDiscoveryThreePidDetailsViewController: UIViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet private weak var threePidBackgroundView: UIView!
@IBOutlet private weak var threePidTitleLabel: UILabel!
@IBOutlet private weak var threePidAdressLabel: UILabel!
@IBOutlet private weak var operationButton: UIButton!
@IBOutlet private weak var informationLabel: UILabel!
// MARK: Private
private var viewModel: SettingsDiscoveryThreePidDetailsViewModelType!
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private weak var presentedAlertController: UIAlertController?
private var displayMode: SettingsDiscoveryThreePidDetailsDisplayMode?
// MARK: - Setup
class func instantiate(with viewModel: SettingsDiscoveryThreePidDetailsViewModelType) -> SettingsDiscoveryThreePidDetailsViewController {
let viewController = StoryboardScene.SettingsDiscoveryThreePidDetailsViewController.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.setupViews()
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView)
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.keyboardAvoider?.startAvoiding()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.keyboardAvoider?.stopAvoiding()
}
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.threePidBackgroundView.backgroundColor = theme.backgroundColor
self.threePidTitleLabel.textColor = theme.textPrimaryColor
self.threePidAdressLabel.textColor = theme.textSecondaryColor
self.informationLabel.textColor = theme.textSecondaryColor
self.operationButton.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.operationButton)
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
self.scrollView.keyboardDismissMode = .interactive
self.render(threePid: self.viewModel.threePid)
}
private func render(threePid: MX3PID) {
let title: String
let threePidTitle: String
let informationText: String
let formattedThreePid: String
switch threePid.medium {
case .email:
title = VectorL10n.settingsDiscoveryThreePidDetailsTitleEmail
threePidTitle = VectorL10n.settingsEmailAddress
informationText = VectorL10n.settingsDiscoveryThreePidDetailsInformationEmail
formattedThreePid = threePid.address
case .msisdn:
title = VectorL10n.settingsDiscoveryThreePidDetailsTitlePhoneNumber
threePidTitle = VectorL10n.settingsPhoneNumber
informationText = VectorL10n.settingsDiscoveryThreePidDetailsInformationPhoneNumber
formattedThreePid = MXKTools.readableMSISDN(threePid.address)
default:
title = ""
threePidTitle = ""
informationText = ""
formattedThreePid = ""
}
self.title = title
self.threePidTitleLabel.text = threePidTitle
self.threePidAdressLabel.text = formattedThreePid
self.informationLabel.text = informationText
}
private func render(viewState: SettingsDiscoveryThreePidDetailsViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded(displayMode: let displayMode):
self.renderLoaded(displayMode: displayMode)
case .error(let error):
self.render(error: error)
}
}
private func renderLoaded(displayMode: SettingsDiscoveryThreePidDetailsDisplayMode) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
let operationButtonTitle: String?
let operationButtonColor: UIColor?
let operationButtonEnabled: Bool
self.presentedAlertController?.dismiss(animated: false, completion: nil)
switch displayMode {
case .share:
operationButtonTitle = VectorL10n.settingsDiscoveryThreePidDetailsShareAction
operationButtonColor = self.theme.tintColor
operationButtonEnabled = true
case .revoke:
operationButtonTitle = VectorL10n.settingsDiscoveryThreePidDetailsRevokeAction
operationButtonColor = self.theme.warningColor
operationButtonEnabled = true
case .pendingThreePidVerification:
switch self.viewModel.threePid.medium {
case .email:
self.presentPendingEmailVerificationAlert()
case .msisdn:
self.presentPendingMSISDNVerificationAlert()
default:
break
}
operationButtonTitle = nil
operationButtonColor = nil
operationButtonEnabled = false
}
if let operationButtonTitle = operationButtonTitle {
self.operationButton.setTitle(operationButtonTitle, for: .normal)
}
if let operationButtonColor = operationButtonColor {
self.operationButton.setTitleColor(operationButtonColor, for: .normal)
}
self.operationButton.isEnabled = operationButtonEnabled
self.displayMode = displayMode
}
private func renderLoading() {
if self.activityPresenter.isPresenting == false {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
self.operationButton.isEnabled = false
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: {
self.viewModel.process(viewAction: .cancelThreePidValidation)
})
self.operationButton.isEnabled = true
}
private func presentPendingEmailVerificationAlert() {
let alert = UIAlertController(title: Bundle.mxk_localizedString(forKey: "account_email_validation_title"),
message: Bundle.mxk_localizedString(forKey: "account_email_validation_message"),
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: VectorL10n.continue, style: .default, handler: { _ in
self.viewModel.process(viewAction: .confirmEmailValidation)
}))
alert.addAction(UIAlertAction(title: Bundle.mxk_localizedString(forKey: "cancel"), style: .cancel, handler: { _ in
self.viewModel.process(viewAction: .cancelThreePidValidation)
}))
self.present(alert, animated: true, completion: nil)
self.presentedAlertController = alert
}
private func presentPendingMSISDNVerificationAlert() {
let alert = UIAlertController(title: Bundle.mxk_localizedString(forKey: "account_msisdn_validation_title"),
message: Bundle.mxk_localizedString(forKey: "account_msisdn_validation_message"),
preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = nil
textField.keyboardType = .phonePad
}
alert.addAction(UIAlertAction(title: VectorL10n.continue, style: .default, handler: { _ in
guard let textField = alert.textFields?.first, let smsCode = textField.text, smsCode.isEmpty == false else {
return
}
self.viewModel.process(viewAction: .confirmMSISDNValidation(code: smsCode))
}))
alert.addAction(UIAlertAction(title: Bundle.mxk_localizedString(forKey: "cancel"), style: .cancel, handler: { _ in
self.viewModel.process(viewAction: .cancelThreePidValidation)
}))
self.present(alert, animated: true, completion: nil)
self.presentedAlertController = alert
}
// MARK: - Actions
@IBAction private func operationButtonAction(_ sender: Any) {
guard let displayMode = self.displayMode else {
return
}
let viewAction: SettingsDiscoveryThreePidDetailsViewAction?
switch displayMode {
case .share:
viewAction = .share
case .revoke:
viewAction = .revoke
default:
viewAction = nil
}
if let viewAction = viewAction {
self.viewModel.process(viewAction: viewAction)
}
}
}
// MARK: - SettingsDiscoveryThreePidDetailsViewModelViewDelegate
extension SettingsDiscoveryThreePidDetailsViewController: SettingsDiscoveryThreePidDetailsViewModelViewDelegate {
func settingsDiscoveryThreePidDetailsViewModel(_ viewModel: SettingsDiscoveryThreePidDetailsViewModelType, didUpdateViewState viewSate: SettingsDiscoveryThreePidDetailsViewState) {
self.render(viewState: viewSate)
}
}

View file

@ -0,0 +1,257 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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
enum SettingsDiscoveryThreePidDetailsViewModelError: Error {
case unknown
}
private struct ThreePidRequestTokenInfo {
let clientSecret: String
let sid: String
let bind: Bool
}
final class SettingsDiscoveryThreePidDetailsViewModel: SettingsDiscoveryThreePidDetailsViewModelType {
// MARK: - Properties
// MARK: Private
private var viewState: SettingsDiscoveryThreePidDetailsViewState?
private let threePidAddManager: MX3PidAddManager
private let identityService: MXIdentityService?
private var currentThreePidAddSession: MX3PidAddSession?
// MARK: Public
let threePid: MX3PID
weak var viewDelegate: SettingsDiscoveryThreePidDetailsViewModelViewDelegate?
// MARK: - Setup
init(session: MXSession, threePid: MX3PID) {
self.threePidAddManager = session.threePidAddManager
self.identityService = session.identityService
self.threePid = threePid
}
// MARK: - Public
func process(viewAction: SettingsDiscoveryThreePidDetailsViewAction) {
switch viewAction {
case .load:
self.load()
case .share:
self.share()
case .revoke:
self.revoke()
case .cancelThreePidValidation:
self.cancelThreePidValidation()
case .confirmEmailValidation:
self.confirmEmailValidation()
case .confirmMSISDNValidation(code: let code):
self.validatePhoneNumber(with: code)
}
}
// MARK: - Private
private func load() {
self.update(viewState: .loading)
self.checkThreePidDiscoverability()
}
private func checkThreePidDiscoverability() {
self.isThreePidDiscoverable(self.threePid) { (response) in
switch response {
case .success(let isDiscoverable):
if isDiscoverable {
self.update(viewState: .loaded(displayMode: .revoke))
} else {
self.update(viewState: .loaded(displayMode: .share))
}
case .failure(let error):
self.update(viewState: .error(error))
}
}
}
private func share() {
self.bind(bind: true)
}
private func revoke() {
self.bind(bind: false)
}
private func bind(bind: Bool) {
self.update(viewState: .loading)
let completion: ((MXResponse<Bool>) -> Void) = { (response) in
switch response {
case .success(let needValidation):
if needValidation {
self.update(viewState: .loaded(displayMode: .pendingThreePidVerification))
if case .email = self.threePid.medium {
self.registerEmailValidationNotification()
}
} else {
self.checkThreePidDiscoverability()
}
case .failure(let error):
self.update(viewState: .error(error))
}
}
switch self.threePid.medium {
case .email:
self.currentThreePidAddSession = self.threePidAddManager.startIdentityServerSession(withEmail: self.threePid.address, bind: bind, completion: completion)
case .msisdn:
let formattedPhoneNumber = self.formattedPhoneNumber(from: threePid.address)
self.currentThreePidAddSession = self.threePidAddManager.startIdentityServerSession(withPhoneNumber: formattedPhoneNumber, countryCode: nil, bind: bind, completion: completion)
default:
break
}
}
@discardableResult
private func isThreePidDiscoverable(_ threePid: MX3PID, completion: @escaping (_ response: MXResponse<Bool>) -> Void) -> MXHTTPOperation? {
guard let identityService = self.identityService else {
completion(.failure(SettingsDiscoveryThreePidDetailsViewModelError.unknown))
return nil
}
return identityService.lookup3PIDs([threePid]) { lookupResponse in
switch lookupResponse {
case .success(let threePids):
completion(.success(threePids.isEmpty == false))
case .failure(let error):
completion(.failure(error))
}
}
}
private func update(viewState: SettingsDiscoveryThreePidDetailsViewState) {
self.viewDelegate?.settingsDiscoveryThreePidDetailsViewModel(self, didUpdateViewState: viewState)
}
// MARK: Email
private func cancelThreePidValidation() {
if case .email = threePid.medium {
self.unregisterEmailValidationNotification()
}
if let currentThreePidAddSession = self.currentThreePidAddSession {
self.threePidAddManager.cancel(session: currentThreePidAddSession)
self.currentThreePidAddSession = nil
}
self.checkThreePidDiscoverability()
}
private func confirmEmailValidation() {
guard let threePidAddSession = self.currentThreePidAddSession else {
return
}
self.update(viewState: .loading)
self.threePidAddManager.tryFinaliseIdentityServerEmailSession(threePidAddSession) { response in
switch response {
case .success:
if threePidAddSession.medium == kMX3PIDMediumEmail {
self.unregisterEmailValidationNotification()
}
self.checkThreePidDiscoverability()
case .failure(let error):
if let mxError = MXError(nsError: error),
(mxError.errcode == kMXErrCodeStringThreePIDAuthFailed
|| mxError.errcode == kMXErrCodeStringUnknown) {
self.update(viewState: .loaded(displayMode: .pendingThreePidVerification))
} else {
if threePidAddSession.medium == kMX3PIDMediumEmail {
self.unregisterEmailValidationNotification()
}
self.update(viewState: .error(error))
}
}
}
}
private func registerEmailValidationNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(handleEmailValidationNotification(notification:)), name: .AppDelegateDidValidateEmail, object: nil)
}
private func unregisterEmailValidationNotification() {
NotificationCenter.default.removeObserver(self, name: .AppDelegateDidValidateEmail, object: nil)
}
@objc private func handleEmailValidationNotification(notification: Notification) {
guard let userInfo = notification.userInfo,
let clientSecret = userInfo[AppDelegateDidValidateEmailNotificationClientSecretKey] as? String,
let sid = userInfo[AppDelegateDidValidateEmailNotificationSIDKey] as? String,
let threePidAddSession = self.currentThreePidAddSession,
threePidAddSession.clientSecret == clientSecret,
threePidAddSession.sid == sid else {
return
}
self.confirmEmailValidation()
}
// MARK: Phone number
private func formattedPhoneNumber(from phoneNumber: String) -> String {
guard phoneNumber.starts(with: "+") == false else {
return phoneNumber
}
return "+\(phoneNumber)"
}
private func validatePhoneNumber(with activationCode: String) {
guard let threePidAddSession = self.currentThreePidAddSession else {
return
}
self.update(viewState: .loading)
self.threePidAddManager.finaliseIdentityServerPhoneNumberSession(threePidAddSession, token: activationCode) { (response) in
switch response {
case .success:
self.checkThreePidDiscoverability()
case .failure(let error):
if let mxError = MXError(nsError: error), mxError.errcode == kMXErrCodeStringUnknownToken {
self.update(viewState: .loaded(displayMode: .pendingThreePidVerification))
} else {
self.update(viewState: .error(error))
}
}
}
}
}

View file

@ -0,0 +1,33 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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 SettingsDiscoveryThreePidDetailsViewModelViewDelegate: class {
func settingsDiscoveryThreePidDetailsViewModel(_ viewModel: SettingsDiscoveryThreePidDetailsViewModelType, didUpdateViewState viewSate: SettingsDiscoveryThreePidDetailsViewState)
}
/// Protocol describing the view model used by `SettingsDiscoveryThreePidDetailsViewController`
protocol SettingsDiscoveryThreePidDetailsViewModelType {
var threePid: MX3PID { get }
var viewDelegate: SettingsDiscoveryThreePidDetailsViewModelViewDelegate? { get set }
func process(viewAction: SettingsDiscoveryThreePidDetailsViewAction)
}

View file

@ -0,0 +1,32 @@
// File created from ScreenTemplate
// $ createScreen.sh Details SettingsDiscoveryThreePidDetails
/*
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
/// SettingsDiscoveryThreePidDetailsViewController view state
enum SettingsDiscoveryThreePidDetailsViewState {
case loading
case loaded(displayMode: SettingsDiscoveryThreePidDetailsDisplayMode)
case error(Error)
}
enum SettingsDiscoveryThreePidDetailsDisplayMode {
case share
case revoke
case pendingThreePidVerification
}

View file

@ -0,0 +1,54 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
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 SettingsIdentityServerCoordinator: SettingsIdentityServerCoordinatorType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let settingsIdentityServerViewController: SettingsIdentityServerViewController
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
// MARK: - Setup
init(session: MXSession) {
self.session = session
let settingsIdentityServerViewModel = SettingsIdentityServerViewModel(session: self.session)
let settingsIdentityServerViewController = SettingsIdentityServerViewController.instantiate(with: settingsIdentityServerViewModel)
self.settingsIdentityServerViewController = settingsIdentityServerViewController
}
// MARK: - Public methods
func start() {
}
func toPresentable() -> UIViewController {
return self.settingsIdentityServerViewController
}
}

View file

@ -0,0 +1,68 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
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 SettingsIdentityServerCoordinatorBridgePresenterDelegate {
func settingsIdentityServerCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SettingsIdentityServerCoordinatorBridgePresenter)
}
/// SettingsIdentityServerCoordinatorBridgePresenter enables to start SettingsIdentityServerCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
@objcMembers
final class SettingsIdentityServerCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var router: NavigationRouter?
private var coordinator: SettingsIdentityServerCoordinator?
// MARK: Public
weak var delegate: SettingsIdentityServerCoordinatorBridgePresenterDelegate?
// MARK: - Setup
init(session: MXSession) {
self.session = session
super.init()
}
// MARK: - Public
func push(from navigationController: UINavigationController, animated: Bool, popCompletion: (() -> Void)?) {
let router = NavigationRouter(navigationController: navigationController)
let settingsIdentityServerCoordinator = SettingsIdentityServerCoordinator(session: self.session)
router.push(settingsIdentityServerCoordinator, animated: animated) { [weak self] in
self?.coordinator = nil
self?.router = nil
popCompletion?()
}
settingsIdentityServerCoordinator.start()
self.coordinator = settingsIdentityServerCoordinator
self.router = router
}
}

View file

@ -0,0 +1,23 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
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
/// `SettingsIdentityServerCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
protocol SettingsIdentityServerCoordinatorType: Coordinator, Presentable {
}

View file

@ -0,0 +1,27 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
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
/// SettingsIdentityServerViewController view actions exposed to view model
enum SettingsIdentityServerViewAction {
case load
case add(identityServer: String)
case change(identityServer: String)
case disconnect
}

View file

@ -0,0 +1,200 @@
<?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="YCb-yR-1Km">
<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>
<!--Settings Identity Server View Controller-->
<scene sceneID="CLh-Ql-FIE">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="YCb-yR-1Km" customClass="SettingsIdentityServerViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="trm-G2-fZS">
<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="8Oz-k6-Zyh">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lG2-Xw-KPE">
<rect key="frame" x="0.0" y="0.0" width="375" height="500"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hwn-qz-R7V">
<rect key="frame" x="0.0" y="0.0" width="375" height="500"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0W7-LW-UNr">
<rect key="frame" x="0.0" y="40" width="375" height="51"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Identity Server" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="g0a-45-iRa">
<rect key="frame" x="20" y="16" width="100" height="19"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="https://vector.im" textAlignment="right" minimumFontSize="15" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="Zwv-XP-gEA">
<rect key="frame" x="140" y="16" width="215" height="19"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits"/>
</textField>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Zwv-XP-gEA" secondAttribute="bottom" constant="16" id="9Um-wU-TXG"/>
<constraint firstItem="Zwv-XP-gEA" firstAttribute="top" secondItem="0W7-LW-UNr" secondAttribute="top" constant="16" id="IAN-Cz-6Rk"/>
<constraint firstItem="Zwv-XP-gEA" firstAttribute="leading" secondItem="g0a-45-iRa" secondAttribute="trailing" constant="20" id="Lga-2A-0Wm"/>
<constraint firstAttribute="trailing" secondItem="Zwv-XP-gEA" secondAttribute="trailing" constant="20" id="WXH-tD-HJA"/>
<constraint firstItem="g0a-45-iRa" firstAttribute="bottom" secondItem="Zwv-XP-gEA" secondAttribute="bottom" id="ccy-C5-fDL"/>
<constraint firstItem="g0a-45-iRa" firstAttribute="top" secondItem="Zwv-XP-gEA" secondAttribute="top" id="eXy-mB-ywA"/>
<constraint firstItem="g0a-45-iRa" firstAttribute="leading" secondItem="0W7-LW-UNr" secondAttribute="leading" constant="20" id="vXp-nX-Q6y"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="You are currently using vector.im to discover and be discoverable by existing contacts you know." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HLL-Fw-u3V">
<rect key="frame" x="20" y="111" width="335" height="36"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h8a-2s-H3K">
<rect key="frame" x="0.0" y="167" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kPC-YQ-MGL">
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="T9R-QE-nBi"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
<state key="normal" title="Change">
<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="addOrChangeButtonAction:" destination="YCb-yR-1Km" eventType="touchUpInside" id="a3m-Lt-6RW"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="kPC-YQ-MGL" secondAttribute="trailing" id="185-hA-OVD"/>
<constraint firstItem="kPC-YQ-MGL" firstAttribute="leading" secondItem="h8a-2s-H3K" secondAttribute="leading" id="OyR-iq-Gs8"/>
<constraint firstItem="kPC-YQ-MGL" firstAttribute="top" secondItem="h8a-2s-H3K" secondAttribute="top" id="W9z-lt-umK"/>
<constraint firstAttribute="bottom" secondItem="kPC-YQ-MGL" secondAttribute="bottom" id="vcz-SU-JOZ"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Jk-ak-Oxh">
<rect key="frame" x="20" y="267" width="335" height="80"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="80" id="L1f-yR-1t3"/>
</constraints>
<string key="text">Disconnecting from your identity server will mean you wont be discoverable by other users and you wont be able to invite others by email or phone.</string>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="O79-u4-Mig">
<rect key="frame" x="0.0" y="367" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Tg-0G-cBC">
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="haa-Jh-EDc"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
<state key="normal" title="Disconnect">
<color key="titleColor" red="1" green="0.0" blue="0.0" 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="disconnectButtonAction:" destination="YCb-yR-1Km" eventType="touchUpInside" id="IJj-ET-fvd"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="1Tg-0G-cBC" secondAttribute="trailing" id="69l-vf-mvU"/>
<constraint firstItem="1Tg-0G-cBC" firstAttribute="leading" secondItem="O79-u4-Mig" secondAttribute="leading" id="9MJ-ja-xpe"/>
<constraint firstItem="1Tg-0G-cBC" firstAttribute="top" secondItem="O79-u4-Mig" secondAttribute="top" id="pYh-MV-LFt"/>
<constraint firstAttribute="bottom" secondItem="1Tg-0G-cBC" secondAttribute="bottom" id="xcG-78-zIp"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="2Jk-ak-Oxh" secondAttribute="trailing" constant="20" id="1wz-cm-ZYv"/>
<constraint firstItem="0W7-LW-UNr" firstAttribute="top" secondItem="hwn-qz-R7V" secondAttribute="top" constant="40" id="7YM-zk-rCu"/>
<constraint firstItem="h8a-2s-H3K" firstAttribute="top" secondItem="HLL-Fw-u3V" secondAttribute="bottom" constant="20" id="8Vh-O9-d29"/>
<constraint firstAttribute="trailing" secondItem="HLL-Fw-u3V" secondAttribute="trailing" constant="20" id="Bs5-Uz-kbF"/>
<constraint firstItem="O79-u4-Mig" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" id="HBn-pM-NdW"/>
<constraint firstItem="O79-u4-Mig" firstAttribute="top" secondItem="2Jk-ak-Oxh" secondAttribute="bottom" constant="20" id="Oz0-aA-1DF"/>
<constraint firstAttribute="trailing" secondItem="O79-u4-Mig" secondAttribute="trailing" id="SzX-d7-T9U"/>
<constraint firstItem="2Jk-ak-Oxh" firstAttribute="top" secondItem="h8a-2s-H3K" secondAttribute="bottom" constant="50" id="TCS-5j-stz"/>
<constraint firstItem="2Jk-ak-Oxh" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" constant="20" id="Wwg-l6-1QO"/>
<constraint firstAttribute="trailing" secondItem="0W7-LW-UNr" secondAttribute="trailing" id="XOC-9V-o8H"/>
<constraint firstItem="HLL-Fw-u3V" firstAttribute="top" secondItem="0W7-LW-UNr" secondAttribute="bottom" constant="20" id="XgK-7i-yhj"/>
<constraint firstItem="h8a-2s-H3K" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" id="b8m-4H-orP"/>
<constraint firstAttribute="width" priority="750" constant="500" id="jEc-ht-fgG"/>
<constraint firstItem="0W7-LW-UNr" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" id="kbX-q7-dtp"/>
<constraint firstItem="HLL-Fw-u3V" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" constant="20" id="pHb-5b-QYN"/>
<constraint firstAttribute="trailing" secondItem="h8a-2s-H3K" secondAttribute="trailing" id="vPw-01-TaS"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="hwn-qz-R7V" firstAttribute="top" secondItem="lG2-Xw-KPE" secondAttribute="top" id="6UK-h7-Jfi"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="hwn-qz-R7V" secondAttribute="trailing" id="PWd-1a-pc6"/>
<constraint firstAttribute="height" constant="500" id="REc-dq-NtM"/>
<constraint firstItem="hwn-qz-R7V" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="lG2-Xw-KPE" secondAttribute="leading" id="Xde-HX-oMG"/>
<constraint firstAttribute="bottom" secondItem="hwn-qz-R7V" secondAttribute="bottom" id="cjT-2e-tp7"/>
<constraint firstItem="hwn-qz-R7V" firstAttribute="centerX" secondItem="lG2-Xw-KPE" secondAttribute="centerX" id="tWw-7s-Eaj"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="lG2-Xw-KPE" secondAttribute="trailing" id="2Oj-q1-byd"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="width" secondItem="8Oz-k6-Zyh" secondAttribute="width" id="2RP-6A-FLF"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="leading" secondItem="8Oz-k6-Zyh" secondAttribute="leading" id="FGn-RZ-dc5"/>
<constraint firstAttribute="bottom" secondItem="lG2-Xw-KPE" secondAttribute="bottom" id="fGL-Ig-G6s"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="top" secondItem="8Oz-k6-Zyh" secondAttribute="top" id="kyx-Ao-xKa"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="8Oz-k6-Zyh" firstAttribute="leading" secondItem="0Gf-da-iK5" secondAttribute="leading" id="059-y1-rPd"/>
<constraint firstAttribute="bottom" secondItem="8Oz-k6-Zyh" secondAttribute="bottom" id="BXd-L8-T6F"/>
<constraint firstItem="0Gf-da-iK5" firstAttribute="top" secondItem="8Oz-k6-Zyh" secondAttribute="top" id="lkR-P4-U2b"/>
<constraint firstItem="0Gf-da-iK5" firstAttribute="trailing" secondItem="8Oz-k6-Zyh" secondAttribute="trailing" id="vL8-o9-5PR"/>
</constraints>
<viewLayoutGuide key="safeArea" id="0Gf-da-iK5"/>
</view>
<connections>
<outlet property="addOrChangeButton" destination="kPC-YQ-MGL" id="Ebk-ko-ay1"/>
<outlet property="addOrChangeButtonContainer" destination="h8a-2s-H3K" id="eEG-rP-zym"/>
<outlet property="disconnectButton" destination="1Tg-0G-cBC" id="iTS-4I-0tT"/>
<outlet property="disconnectButtonContainer" destination="O79-u4-Mig" id="KKh-kb-kKX"/>
<outlet property="disconnectMessageLabel" destination="2Jk-ak-Oxh" id="ktM-yq-jWv"/>
<outlet property="identityServerContainer" destination="0W7-LW-UNr" id="wTi-zd-vJH"/>
<outlet property="identityServerLabel" destination="g0a-45-iRa" id="3n9-g9-uYC"/>
<outlet property="identityServerTextField" destination="Zwv-XP-gEA" id="5ci-zn-EQ6"/>
<outlet property="messageLabel" destination="HLL-Fw-u3V" id="apO-wG-hi7"/>
<outlet property="scrollView" destination="8Oz-k6-Zyh" id="e5g-xe-zBj"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="s4k-Y2-H87" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3772" y="-774"/>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,413 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
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 SettingsIdentityServerViewController: UIViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet weak var identityServerContainer: UIView!
@IBOutlet weak var identityServerLabel: UILabel!
@IBOutlet weak var identityServerTextField: UITextField!
@IBOutlet private weak var messageLabel: UILabel!
@IBOutlet weak var addOrChangeButtonContainer: UIView!
@IBOutlet private weak var addOrChangeButton: UIButton!
@IBOutlet weak var disconnectMessageLabel: UILabel!
@IBOutlet weak var disconnectButtonContainer: UIView!
@IBOutlet weak var disconnectButton: UIButton!
// MARK: Private
private var viewModel: SettingsIdentityServerViewModelType!
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private var viewState: SettingsIdentityServerViewState?
private var displayMode: SettingsIdentityServerDisplayMode?
private weak var alertController: UIAlertController?
private var serviceTermsModalCoordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter?
private var serviceTermsModalCoordinatorBridgePresenterOnComplete: ((Bool) -> Void)?
// MARK: - Setup
class func instantiate(with viewModel: SettingsIdentityServerViewModelType) -> SettingsIdentityServerViewController {
let viewController = StoryboardScene.SettingsIdentityServerViewController.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.identityServerSettingsTitle
self.setupViews()
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView)
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.keyboardAvoider?.startAvoiding()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.keyboardAvoider?.stopAvoiding()
}
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.identityServerContainer.backgroundColor = theme.backgroundColor
self.identityServerLabel.textColor = theme.textPrimaryColor
theme.applyStyle(onTextField: self.identityServerTextField)
self.identityServerTextField.textColor = theme.textSecondaryColor
self.messageLabel.textColor = theme.textPrimaryColor
self.addOrChangeButtonContainer.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.addOrChangeButton)
self.disconnectMessageLabel.textColor = theme.textPrimaryColor
self.disconnectButtonContainer.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.disconnectButton)
self.disconnectButton.setTitleColor(self.theme.warningColor, for: .normal)
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
self.scrollView.keyboardDismissMode = .interactive
self.identityServerLabel.text = VectorL10n.identityServerSettingsTitle
self.identityServerTextField.placeholder = VectorL10n.identityServerSettingsPlaceHolder
self.identityServerTextField.keyboardType = .URL
self.identityServerTextField.addTarget(self, action: #selector(identityServerTextFieldDidChange(_:)), for: .editingChanged)
self.identityServerTextField.addTarget(self, action: #selector(identityServerTextFieldDidEndOnExit(_:)), for: .editingDidEndOnExit)
self.disconnectMessageLabel.text = VectorL10n.identityServerSettingsDisconnectInfo
self.disconnectButton.setTitle(VectorL10n.identityServerSettingsDisconnect, for: .normal)
self.disconnectButton.setTitle(VectorL10n.identityServerSettingsDisconnect, for: .highlighted)
}
private func render(viewState: SettingsIdentityServerViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded(let displayMode):
self.renderLoaded(displayMode: displayMode)
case .presentTerms(let session, let accessToken, let baseUrl, let onComplete):
self.presentTerms(session: session, accessToken: accessToken, baseUrl: baseUrl, onComplete: onComplete)
case .alert(let alert, let onContinue):
self.renderAlert(alert: alert, onContinue: onContinue)
case .error(let error):
self.render(error: error)
}
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderLoaded(displayMode: SettingsIdentityServerDisplayMode) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.displayMode = displayMode
switch displayMode {
case .noIdentityServer:
self.renderNoIdentityServer()
case .identityServer(let host):
self.renderIdentityServer(host: host)
}
}
private func renderNoIdentityServer() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.identityServerTextField.text = nil
self.messageLabel.text = VectorL10n.identityServerSettingsNoIsDescription
self.addOrChangeButton.setTitle(VectorL10n.identityServerSettingsAdd, for: .normal)
self.addOrChangeButton.setTitle(VectorL10n.identityServerSettingsAdd, for: .highlighted)
self.addOrChangeButton.isEnabled = false
self.disconnectMessageLabel.isHidden = true
self.disconnectButtonContainer.isHidden = true
}
private func renderIdentityServer(host: String) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.identityServerTextField.text = host
self.messageLabel.text = VectorL10n.identityServerSettingsDescription(host.hostname())
self.addOrChangeButton.setTitle(VectorL10n.identityServerSettingsChange, for: .normal)
self.addOrChangeButton.setTitle(VectorL10n.identityServerSettingsChange, for: .highlighted)
self.addOrChangeButton.isEnabled = false
self.disconnectMessageLabel.isHidden = false
self.disconnectButtonContainer.isHidden = false
}
private func presentTerms(session: MXSession, accessToken: String, baseUrl: String, onComplete: @escaping (Bool) -> Void) {
let serviceTermsModalCoordinatorBridgePresenter = ServiceTermsModalCoordinatorBridgePresenter(session: session, baseUrl: baseUrl, serviceType: MXServiceTypeIdentityService, accessToken: accessToken)
serviceTermsModalCoordinatorBridgePresenter.present(from: self, animated: true)
serviceTermsModalCoordinatorBridgePresenter.delegate = self
self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter
self.serviceTermsModalCoordinatorBridgePresenterOnComplete = onComplete
}
private func hideTerms(accepted: Bool) {
guard let serviceTermsModalCoordinatorBridgePresenterOnComplete = self.serviceTermsModalCoordinatorBridgePresenterOnComplete else {
return
}
self.serviceTermsModalCoordinatorBridgePresenter?.dismiss(animated: true, completion: nil)
self.serviceTermsModalCoordinatorBridgePresenter = nil
serviceTermsModalCoordinatorBridgePresenterOnComplete(accepted)
self.serviceTermsModalCoordinatorBridgePresenterOnComplete = nil
}
private func renderAlert(alert: SettingsIdentityServerAlert, onContinue: @escaping () -> Void) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
switch alert {
case .addActionAlert(.invalidIdentityServer(let newHost)),
.changeActionAlert(.invalidIdentityServer(let newHost)):
self.showAlert(title: nil,
message: VectorL10n.identityServerSettingsAlertErrorInvalidIdentityServer(newHost),
continueButtonTitle: nil,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .addActionAlert(.noTerms),
.changeActionAlert(.noTerms):
self.showAlert(title: VectorL10n.identityServerSettingsAlertNoTermsTitle,
message: VectorL10n.identityServerSettingsAlertNoTerms,
continueButtonTitle: VectorL10n.continue,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .addActionAlert(.termsNotAccepted(let newHost)),
.changeActionAlert(.termsNotAccepted(let newHost)):
self.showAlert(title: nil,
message: VectorL10n.identityServerSettingsAlertErrorTermsNotAccepted(newHost.hostname()),
continueButtonTitle: nil,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .changeActionAlert(.stillSharing3Pids(let oldHost, _)):
self.showAlert(title: VectorL10n.identityServerSettingsAlertChangeTitle,
message: VectorL10n.identityServerSettingsAlertDisconnectStillSharing3pid(oldHost.hostname()),
continueButtonTitle: VectorL10n.identityServerSettingsAlertDisconnectStillSharing3pidButton,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .changeActionAlert(.doubleConfirmation(let oldHost, let newHost)):
self.showAlert(title: VectorL10n.identityServerSettingsAlertChangeTitle,
message: VectorL10n.identityServerSettingsAlertChange(oldHost.hostname(), newHost.hostname()),
continueButtonTitle: VectorL10n.continue,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .disconnectActionAlert(.stillSharing3Pids(let oldHost)):
self.showAlert(title: VectorL10n.identityServerSettingsAlertDisconnectTitle,
message: VectorL10n.identityServerSettingsAlertDisconnectStillSharing3pid(oldHost.hostname()),
continueButtonTitle: VectorL10n.identityServerSettingsAlertDisconnectStillSharing3pidButton,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
case .disconnectActionAlert(.doubleConfirmation(let oldHost)):
self.showAlert(title: VectorL10n.identityServerSettingsAlertDisconnectTitle,
message: VectorL10n.identityServerSettingsAlertDisconnect(oldHost.hostname()),
continueButtonTitle: VectorL10n.identityServerSettingsAlertDisconnectButton,
cancelButtonTitle: VectorL10n.cancel,
onContinue: onContinue)
}
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
// MARK: - Alert
private func showAlert(title: String?, message: String, continueButtonTitle: String?, cancelButtonTitle: String, onContinue: @escaping () -> Void) {
guard self.alertController == nil else {
return
}
let alertController = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: cancelButtonTitle, style: .cancel, handler: { action in
}))
if let continueButtonTitle = continueButtonTitle {
alertController.addAction(UIAlertAction(title: continueButtonTitle, style: .default, handler: { action in
onContinue()
}))
}
self.present(alertController, animated: true, completion: nil)
self.alertController = alertController
}
private func hideAlert(animated: Bool) {
self.alertController?.dismiss(animated: true, completion: nil)
}
// MARK: - Actions
@objc private func identityServerTextFieldDidChange(_ textField: UITextField) {
self.addOrChangeButton.isEnabled = textField.text?.count ?? 0 > 0
&& (textField.text?.hostname() != self.viewModel.identityServer?.hostname())
}
@objc private func identityServerTextFieldDidEndOnExit(_ textField: UITextField) {
self.addOrChangeAction()
}
@IBAction private func addOrChangeButtonAction(_ sender: Any) {
self.addOrChangeAction()
}
private func addOrChangeAction() {
self.identityServerTextField.resignFirstResponder()
guard let displayMode = self.displayMode, let identityServer = self.identityServerTextField.text else {
return
}
let viewAction: SettingsIdentityServerViewAction?
switch displayMode {
case .noIdentityServer:
viewAction = .add(identityServer: identityServer.makeURLValid())
case .identityServer:
viewAction = .change(identityServer: identityServer.makeURLValid())
}
if let viewAction = viewAction {
self.viewModel.process(viewAction: viewAction)
}
}
@IBAction private func disconnectButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .disconnect)
}
}
// MARK: - SettingsIdentityServerViewModelViewDelegate
extension SettingsIdentityServerViewController: SettingsIdentityServerViewModelViewDelegate {
func settingsIdentityServerViewModel(_ viewModel: SettingsIdentityServerViewModelType, didUpdateViewState viewState: SettingsIdentityServerViewState) {
self.viewState = viewState
self.render(viewState: viewState)
}
}
// MARK: - ServiceTermsModalCoordinatorBridgePresenterDelegate
extension SettingsIdentityServerViewController: ServiceTermsModalCoordinatorBridgePresenterDelegate {
func serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter) {
self.hideTerms(accepted: true)
}
func serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter, session: MXSession) {
self.hideTerms(accepted: false)
}
func serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: ServiceTermsModalCoordinatorBridgePresenter) {
self.hideTerms(accepted: false)
}
}
// MARK: - Private extension
fileprivate extension String {
func hostname() -> String {
return URL(string: self)?.host ?? self
}
func makeURLValid() -> String {
if self.hasPrefix("http://") || self.hasPrefix("https://") {
return self
} else {
return "https://" + self
}
}
}

View file

@ -0,0 +1,410 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
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
enum SettingsIdentityServerViewModelError: Error {
case missingIdentityServer
case unknown
}
enum IdentityServerTermsStatus {
case noTerms
case terms(agreed: Bool)
}
enum IdentityServerValidity {
case invalid
case valid(status: IdentityServerTermsStatus)
}
final class SettingsIdentityServerViewModel: SettingsIdentityServerViewModelType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var identityService: MXIdentityService?
private var serviceTerms: MXServiceTerms?
// MARK: Public
weak var viewDelegate: SettingsIdentityServerViewModelViewDelegate?
var identityServer: String?
// MARK: - Setup
init(session: MXSession) {
self.session = session
}
// MARK: - Public
func process(viewAction: SettingsIdentityServerViewAction) {
switch viewAction {
case .load:
self.load()
case .add(identityServer: let identityServer):
self.addIdentityServer(identityServer)
case .change(identityServer: let identityServer):
self.changeIdentityServer(identityServer)
case .disconnect:
self.disconnect()
}
}
// MARK: - Private -
// MARK: - Actions
private func load() {
self.refreshIdentityServerViewState()
}
// MARK: Add IS
private func addIdentityServer(_ newIdentityServer: String) {
self.checkCanAddIdentityServer(newIdentityServer: newIdentityServer,
viewStateUpdate: { (viewState) in
self.update(viewState: viewState)
},
canAddcompletion: {
self.updateIdentityServerAndRefreshViewState(with: newIdentityServer)
})
}
private func checkCanAddIdentityServer(newIdentityServer: String,
viewStateUpdate: @escaping (SettingsIdentityServerViewState) -> Void,
canAddcompletion: @escaping(() -> Void)) {
viewStateUpdate(.loading)
self.checkIdentityServerValidity(identityServer: newIdentityServer) { (identityServerValidityResponse) in
print("[SettingsIdentityServerViewModel] checkCanAddIdentityServer: \(newIdentityServer). Validity: \(identityServerValidityResponse)")
switch identityServerValidityResponse {
case .success(let identityServerValidity):
switch identityServerValidity {
case .invalid:
// Present invalid IS alert
viewStateUpdate(.alert(alert: SettingsIdentityServerAlert.addActionAlert(.invalidIdentityServer(newHost: newIdentityServer)), onContinue: {}))
case .valid(status: let termsStatus):
switch termsStatus {
case .noTerms:
viewStateUpdate(.alert(alert: SettingsIdentityServerAlert.addActionAlert(.noTerms(newHost: newIdentityServer)), onContinue: {
viewStateUpdate(.loading)
canAddcompletion()
}))
case .terms(agreed: let termsAgreed):
if termsAgreed {
canAddcompletion()
} else {
self.accessToken(identityServer: newIdentityServer) { (response) in
switch response {
case .success(let accessToken):
guard let accessToken = accessToken else {
print("[SettingsIdentityServerViewModel] accessToken: Error: No access token")
viewStateUpdate(.error(SettingsIdentityServerViewModelError.unknown))
return
}
// Present terms
viewStateUpdate(.presentTerms(session: self.session, accessToken: accessToken, baseUrl: newIdentityServer, onComplete: { (areTermsAccepted) in
if areTermsAccepted {
canAddcompletion()
} else {
viewStateUpdate(.alert(alert: SettingsIdentityServerAlert.addActionAlert(.termsNotAccepted(newHost: newIdentityServer)), onContinue: {}))
}
}))
case .failure(let error):
self.update(viewState: .error(error))
}
}
}
}
}
case .failure(let error):
viewStateUpdate(.error(error))
}
}
}
// MARK: Change IS
private func changeIdentityServer(_ newIdentityServer: String) {
guard let identityServer = self.identityServer else {
return
}
let viewStateUpdate: (SettingsIdentityServerViewState) -> Void = { (viewState) in
// Convert states for .addActionAlert and .disconnectActionAlert to
//.changeActionAlert
var changeViewState = viewState
switch viewState {
case .alert(let alert, let onContinue):
switch alert {
case .addActionAlert(.invalidIdentityServer(let newHost)):
changeViewState = .alert(
alert: SettingsIdentityServerAlert.changeActionAlert(.invalidIdentityServer(newHost: newHost)),
onContinue: onContinue)
case .addActionAlert(.noTerms(let newHost)):
changeViewState = .alert(
alert: SettingsIdentityServerAlert.changeActionAlert(.noTerms(newHost: newHost)),
onContinue: onContinue)
case .addActionAlert(.termsNotAccepted(let newHost)):
changeViewState = .alert(
alert: SettingsIdentityServerAlert.changeActionAlert(.termsNotAccepted(newHost: newHost)),
onContinue: onContinue)
case .disconnectActionAlert(.stillSharing3Pids(let oldHost)):
changeViewState = .alert(
alert: SettingsIdentityServerAlert.changeActionAlert(.stillSharing3Pids(oldHost: oldHost, newHost: newIdentityServer)),
onContinue: onContinue)
case .disconnectActionAlert(.doubleConfirmation(let oldHost)):
changeViewState = .alert(
alert: SettingsIdentityServerAlert.changeActionAlert(.doubleConfirmation(oldHost: oldHost, newHost: newIdentityServer)),
onContinue: onContinue)
default:
break
}
default:
break
}
self.update(viewState: changeViewState)
}
self.checkCanAddIdentityServer(newIdentityServer: newIdentityServer, viewStateUpdate: viewStateUpdate) {
self.checkCanDisconnectIdentityServer(identityServer: identityServer, viewStateUpdate: viewStateUpdate, canDisconnectCompletion: {
self.update(viewState: .loading)
self.disconnectIdentityServer(refreshViewState: false)
self.updateIdentityServerAndRefreshViewState(with: newIdentityServer)
})
}
}
// MARK: Disconnect IS
private func disconnect() {
guard let identityServer = self.identityServer else {
return
}
self.checkCanDisconnectIdentityServer(identityServer: identityServer,
viewStateUpdate: { (viewState) in
self.update(viewState: viewState)
},
canDisconnectCompletion: {
self.update(viewState: .loading)
self.disconnectIdentityServer()
})
}
private func disconnectIdentityServer(refreshViewState: Bool = true) {
// TODO: Make a /account/logout request
if refreshViewState {
self.updateIdentityServerAndRefreshViewState(with: nil)
}
}
private func checkCanDisconnectIdentityServer(identityServer: String,
viewStateUpdate: @escaping (SettingsIdentityServerViewState) -> Void,
canDisconnectCompletion: @escaping(() -> Void)) {
self.update(viewState: .loading)
self.checkExistingDataOnIdentityServer { (response) in
switch response {
case .success(let existingData):
if existingData {
viewStateUpdate(.alert(alert: SettingsIdentityServerAlert.disconnectActionAlert(.stillSharing3Pids(oldHost: identityServer)),
onContinue: canDisconnectCompletion))
} else {
viewStateUpdate(.alert(alert: SettingsIdentityServerAlert.disconnectActionAlert(.doubleConfirmation(oldHost: identityServer)),
onContinue: canDisconnectCompletion))
}
case .failure(let error):
viewStateUpdate( .error(error))
}
}
}
// MARK: - Model update
private func update(viewState: SettingsIdentityServerViewState) {
self.viewDelegate?.settingsIdentityServerViewModel(self, didUpdateViewState: viewState)
}
private func updateIdentityServerAndRefreshViewState(with identityServer: String?) {
self.accessToken(identityServer: identityServer) { (response) in
switch response {
case .success(let accessToken):
self.session.setIdentityServer(identityServer, andAccessToken: accessToken)
self.session.setAccountDataIdentityServer(identityServer, success: {
self.refreshIdentityServerViewState()
}, failure: { error in
self.update(viewState: .error(error ?? SettingsIdentityServerViewModelError.unknown))
})
case .failure(let error):
self.update(viewState: .error(error))
}
}
}
private func refreshIdentityServerViewState() {
if let identityService = self.session.identityService {
let host = identityService.identityServer
self.identityServer = host
self.update(viewState: .loaded(displayMode: .identityServer(host: host)))
} else {
self.update(viewState: .loaded(displayMode: .noIdentityServer))
}
}
// MARK: - Helpers
private func checkExistingDataOnIdentityServer(completion: @escaping (_ response: MXResponse<Bool>) -> Void) {
self.session.matrixRestClient.thirdPartyIdentifiers { (thirdPartyIDresponse) in
switch thirdPartyIDresponse {
case .success(let thirdPartyIdentifiers):
guard let thirdPartyIdentifiers = thirdPartyIdentifiers else {
completion(.success(false))
return
}
if thirdPartyIdentifiers.isEmpty {
completion(.success(false))
} else {
let mx3Pids = SettingsIdentityServerViewModel.threePids(from: thirdPartyIdentifiers)
self.areThereThreePidsDiscoverable(mx3Pids, completion: { discoverable3pidsResponse in
switch discoverable3pidsResponse {
case .success(let isThereDiscoverable3pids):
completion(.success(isThereDiscoverable3pids))
case .failure(let error):
completion(.failure(error))
}
})
}
case .failure(let error):
completion(.failure(error))
}
}
}
private func accessToken(identityServer: String?, completion: @escaping (_ response: MXResponse<String?>) -> Void) {
guard let identityServer = identityServer, let identityServerURL = URL(string: identityServer) else {
completion(.success(nil))
return
}
let restClient: MXRestClient = self.session.matrixRestClient
let identityService = MXIdentityService(identityServer: identityServerURL, accessToken: nil, homeserverRestClient: restClient)
identityService.accessToken { (response) in
self.identityService = nil
completion(response)
}
self.identityService = identityService
}
@discardableResult
private func areThereThreePidsDiscoverable(_ threePids: [MX3PID], completion: @escaping (_ response: MXResponse<Bool>) -> Void) -> MXHTTPOperation? {
guard let identityService = self.session.identityService else {
completion(.failure(SettingsIdentityServerViewModelError.missingIdentityServer))
return nil
}
return identityService.lookup3PIDs(threePids) { lookupResponse in
switch lookupResponse {
case .success(let threePids):
completion(.success(threePids.isEmpty == false))
case .failure(let error):
completion(.failure(error))
}
}
}
private func checkIdentityServerValidity(identityServer: String, completion: @escaping (_ response: MXResponse<IdentityServerValidity>) -> Void) {
guard let identityServerURL = URL(string: identityServer) else {
completion(.success(.invalid))
return
}
let restClient: MXRestClient = self.session.matrixRestClient
let identityService = MXIdentityService(identityServer: identityServerURL, accessToken: nil, homeserverRestClient: restClient)
// First, check the server
identityService.pingIdentityServer { response in
self.identityService = nil
switch response {
case .success:
// Them, check if there are terms to accept
let serviceTerms = MXServiceTerms(baseUrl: identityService.identityServer, serviceType: MXServiceTypeIdentityService, matrixSession: self.session, accessToken: nil)
serviceTerms.areAllTermsAgreed({ (agreedTermsProgress) in
self.serviceTerms = nil
if agreedTermsProgress.totalUnitCount == 0 {
completion(.success(IdentityServerValidity.valid(status: .noTerms)))
} else {
completion(.success(IdentityServerValidity.valid(status: .terms(agreed: agreedTermsProgress.isFinished))))
}
}, failure: { (error) in
self.serviceTerms = nil
completion(.failure(error))
})
self.serviceTerms = serviceTerms
case .failure(let error):
guard let nsError = error as NSError? else {
completion(.failure(error))
return
}
if nsError.domain == MXIdentityServerRestClientErrorDomain
|| (nsError.domain == NSURLErrorDomain
&& (nsError.code == NSURLErrorCannotFindHost
|| nsError.code == NSURLErrorCancelled)) {
completion(.success(.invalid))
} else {
completion(.failure(error))
}
}
}
self.identityService = identityService
}
private class func threePids(from thirdPartyIdentifiers: [MXThirdPartyIdentifier]) -> [MX3PID] {
return thirdPartyIdentifiers.map({ (thirdPartyIdentifier) -> MX3PID in
return MX3PID(medium: MX3PID.Medium(identifier: thirdPartyIdentifier.medium), address: thirdPartyIdentifier.address)
})
}
}

View file

@ -0,0 +1,33 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
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 SettingsIdentityServerViewModelViewDelegate: class {
func settingsIdentityServerViewModel(_ viewModel: SettingsIdentityServerViewModelType, didUpdateViewState viewSate: SettingsIdentityServerViewState)
}
/// Protocol describing the view model used by `SettingsIdentityServerViewController`
protocol SettingsIdentityServerViewModelType {
var viewDelegate: SettingsIdentityServerViewModelViewDelegate? { get set }
var identityServer: String? { get }
func process(viewAction: SettingsIdentityServerViewAction)
}

View file

@ -0,0 +1,60 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
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
/// SettingsIdentityServerViewController view state
enum SettingsIdentityServerViewState {
case loading
case loaded(displayMode: SettingsIdentityServerDisplayMode)
case presentTerms(session: MXSession, accessToken: String, baseUrl: String, onComplete: (Bool) -> Void)
case alert(alert: SettingsIdentityServerAlert, onContinue: () -> Void)
case error(Error)
}
enum SettingsIdentityServerDisplayMode {
case noIdentityServer
case identityServer(host: String)
}
/// Alerts that can be presented when the user triggered an action among SettingsIdentityServerViewAction.
/// These alerts allow interaction with the user to complete the action flow.
enum SettingsIdentityServerAlert {
case addActionAlert(AddActionAlert)
case changeActionAlert(ChangeActionAlert)
case disconnectActionAlert(DisconnectActionAlert)
enum AddActionAlert {
case invalidIdentityServer(newHost: String)
case noTerms(newHost: String)
case termsNotAccepted(newHost: String)
}
enum ChangeActionAlert {
case invalidIdentityServer(newHost: String)
case noTerms(newHost: String)
case termsNotAccepted(newHost: String)
case stillSharing3Pids(oldHost: String, newHost: String)
case doubleConfirmation(oldHost: String, newHost: String)
}
enum DisconnectActionAlert {
case stillSharing3Pids(oldHost: String)
case doubleConfirmation(oldHost: String)
}
}

View file

@ -53,9 +53,11 @@ enum
SETTINGS_SECTION_USER_SETTINGS_INDEX,
SETTINGS_SECTION_NOTIFICATIONS_SETTINGS_INDEX,
SETTINGS_SECTION_CALLS_INDEX,
SETTINGS_SECTION_USER_INTERFACE_INDEX,
SETTINGS_SECTION_IGNORED_USERS_INDEX,
SETTINGS_SECTION_DISCOVERY_INDEX,
SETTINGS_SECTION_IDENTITY_SERVER_INDEX,
SETTINGS_SECTION_CONTACTS_INDEX,
SETTINGS_SECTION_IGNORED_USERS_INDEX,
SETTINGS_SECTION_USER_INTERFACE_INDEX,
SETTINGS_SECTION_ADVANCED_INDEX,
SETTINGS_SECTION_OTHER_INDEX,
SETTINGS_SECTION_LABS_INDEX,
@ -86,7 +88,9 @@ enum
enum
{
CALLS_ENABLE_CALLKIT_INDEX = 0,
CALLS_DESCRIPTION_INDEX,
CALLS_CALLKIT_DESCRIPTION_INDEX,
CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX,
CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX,
CALLS_COUNT
};
@ -97,6 +101,13 @@ enum
USER_INTERFACE_COUNT
};
enum
{
IDENTITY_SERVER_INDEX,
IDENTITY_SERVER_DESCRIPTION_INDEX,
IDENTITY_SERVER_COUNT
};
enum
{
OTHER_VERSION_INDEX = 0,
@ -128,6 +139,11 @@ enum {
CRYPTOGRAPHY_COUNT
};
enum
{
DEVICES_DESCRIPTION_INDEX = 0
};
#define SECTION_TITLE_PADDING_WHEN_HIDDEN 0.01f
typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
@ -139,7 +155,9 @@ MXKEncryptionInfoViewDelegate,
KeyBackupSetupCoordinatorBridgePresenterDelegate,
KeyBackupRecoverCoordinatorBridgePresenterDelegate,
SignOutAlertPresenterDelegate,
SingleImagePickerPresenterDelegate>
SingleImagePickerPresenterDelegate,
SettingsDiscoveryTableViewSectionDelegate, SettingsDiscoveryViewModelCoordinatorDelegate,
SettingsIdentityServerCoordinatorBridgePresenterDelegate>
{
// Current alert (if any).
UIAlertController *currentAlert;
@ -186,6 +204,7 @@ SingleImagePickerPresenterDelegate>
NSInteger userSettingsPhoneStartIndex; // The user can have several linked phone numbers. Hence, the dynamic section items count
NSInteger userSettingsNewPhoneIndex; // This index also marks the end of the phone numbers list
NSInteger userSettingsChangePasswordIndex;
NSInteger userSettingsThreePidsInformation;
NSInteger userSettingsNightModeSepIndex;
NSInteger userSettingsNightModeIndex;
@ -232,6 +251,8 @@ SingleImagePickerPresenterDelegate>
SettingsKeyBackupTableViewSection *keyBackupSection;
KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter;
KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter;
SettingsIdentityServerCoordinatorBridgePresenter *identityServerSettingsCoordinatorBridgePresenter;
}
/**
@ -249,6 +270,10 @@ SingleImagePickerPresenterDelegate>
@property (nonatomic, weak) UIButton *signOutButton;
@property (nonatomic, strong) SingleImagePickerPresenter *imagePickerPresenter;
@property (nonatomic, strong) SettingsDiscoveryViewModel *settingsDiscoveryViewModel;
@property (nonatomic, strong) SettingsDiscoveryTableViewSection *settingsDiscoveryTableViewSection;
@property (nonatomic, strong) SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter *discoveryThreePidDetailsPresenter;
@end
@implementation SettingsViewController
@ -316,6 +341,8 @@ SingleImagePickerPresenterDelegate>
}];
[self registerAccountDataDidChangeIdentityServerNotification];
// Add each matrix session, to update the view controller appearance according to mx sessions state
NSArray *sessions = [AppDelegate theDelegate].mxSessions;
for (MXSession *mxSession in sessions)
@ -335,6 +362,8 @@ SingleImagePickerPresenterDelegate>
}
}
[self setupDiscoverySection];
groupsDataSource = [[GroupsDataSource alloc] initWithMatrixSession:self.mainSession];
[groupsDataSource finalizeInitialization];
groupsDataSource.delegate = self;
@ -431,6 +460,7 @@ SingleImagePickerPresenterDelegate>
keyBackupSetupCoordinatorBridgePresenter = nil;
keyBackupRecoverCoordinatorBridgePresenter = nil;
identityServerSettingsCoordinatorBridgePresenter = nil;
}
- (void)onMatrixSessionStateDidChange:(NSNotification *)notif
@ -481,6 +511,13 @@ SingleImagePickerPresenterDelegate>
newPhoneNumberCountryPicker = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.settingsDiscoveryTableViewSection reload];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
@ -658,47 +695,28 @@ SingleImagePickerPresenterDelegate>
}
}
- (void)showValidationEmailDialogWithMessage:(NSString*)message for3PID:(MXK3PID*)threePID
- (void)showValidationEmailDialogWithMessage:(NSString*)message for3PidAddSession:(MX3PidAddSession*)threePidAddSession threePidAddManager:(MX3PidAddManager*)threePidAddManager
{
__weak typeof(self) weakSelf = self;
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"account_email_validation_title"] message:message preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"abort"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self stopActivityIndicator];
// Reset new email adding
self.newEmailEditingEnabled = NO;
}
}]];
__strong __typeof(threePID)strongThreePID = threePID;
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"continue"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"continue"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = YES;
// We always bind emails when registering, so let's do the same here
[threePID add3PIDToUser:YES success:^{
[threePidAddManager tryFinaliseAddEmailSession:threePidAddSession success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during email binding
@ -720,15 +738,10 @@ SingleImagePickerPresenterDelegate>
// Update linked emails
[self loadAccount3PIDs];
}
}
} failure:^(NSError *error) {
} failure:^(NSError * _Nonnull error) {
NSLog(@"[SettingsViewController] Failed to bind email");
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during email binding
@ -746,7 +759,9 @@ SingleImagePickerPresenterDelegate>
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringThreePIDAuthFailed])
{
[self showValidationEmailDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_email_validation_error"] for3PID:strongThreePID];
[self showValidationEmailDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_email_validation_error"]
for3PidAddSession:threePidAddSession
threePidAddManager:threePidAddManager];
}
else
{
@ -757,39 +772,29 @@ SingleImagePickerPresenterDelegate>
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}
}
}];
}
}]];
[currentAlert mxk_setAccessibilityIdentifier:@"SettingsVCEmailValidationAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
}
- (void)showValidationMsisdnDialogWithMessage:(NSString*)message for3PID:(MXK3PID*)threePID
- (void)showValidationMsisdnDialogWithMessage:(NSString*)message for3PidAddSession:(MX3PidAddSession*)threePidAddSession threePidAddManager:(MX3PidAddManager*)threePidAddManager
{
__weak typeof(self) weakSelf = self;
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"account_msisdn_validation_title"] message:message preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"abort"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self stopActivityIndicator];
// Reset new phone adding
self.newPhoneEditingEnabled = NO;
}
}]];
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
@ -798,13 +803,9 @@ SingleImagePickerPresenterDelegate>
textField.keyboardType = UIKeyboardTypeDecimalPad;
}];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"submit"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"submit"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
MXStrongifyAndReturnIfNil(self);
NSString *smsCode = [self->currentAlert textFields].firstObject.text;
@ -814,14 +815,8 @@ SingleImagePickerPresenterDelegate>
{
self->is3PIDBindingInProgress = YES;
[threePID submitValidationToken:smsCode success:^{
[threePidAddManager finaliseAddPhoneNumberSession:threePidAddSession withToken:smsCode success:^{
// We always bind the phone numbers when registering, so let's do the same here
[threePID add3PIDToUser:YES success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during the binding
@ -841,42 +836,11 @@ SingleImagePickerPresenterDelegate>
// Update linked 3pids
[self loadAccount3PIDs];
}
}
} failure:^(NSError *error) {
NSLog(@"[SettingsViewController] Failed to bind phone number");
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during phone binding
if (self->onReadyToDestroyHandler)
{
// Ready to destroy
self->onReadyToDestroyHandler();
self->onReadyToDestroyHandler = nil;
}
else
{
[self stopActivityIndicator];
NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}
}];
} failure:^(NSError *error) {
} failure:^(NSError * _Nonnull error) {
NSLog(@"[SettingsViewController] Failed to submit the sms token");
if (weakSelf)
{
typeof(self) self = weakSelf;
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during phone binding
@ -915,32 +879,22 @@ SingleImagePickerPresenterDelegate>
self->currentAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Ask again the sms token
[self showValidationMsisdnDialogWithMessage:message for3PID:threePID];
}
[self showValidationMsisdnDialogWithMessage:message for3PidAddSession:threePidAddSession threePidAddManager:threePidAddManager];
}]];
[self->currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCErrorAlert"];
[self presentViewController:self->currentAlert animated:YES completion:nil];
}
}
}];
}
else
{
// Ask again the sms token
[self showValidationMsisdnDialogWithMessage:message for3PID:threePID];
[self showValidationMsisdnDialogWithMessage:message for3PidAddSession:threePidAddSession threePidAddManager:threePidAddManager];
}
}
}]];
[currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCMsisdnValidationAlert"];
@ -953,6 +907,9 @@ SingleImagePickerPresenterDelegate>
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
[account load3PIDs:^{
NSArray<MXThirdPartyIdentifier*> *thirdPartyIdentifiers = account.threePIDs ?: @[];
[self.settingsDiscoveryViewModel updateWithThirdPartyIdentifiers:thirdPartyIdentifiers];
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
// Note: The use of 'reloadData' handles the case where the account has been logged out.
[self refreshSettings];
@ -1008,6 +965,10 @@ SingleImagePickerPresenterDelegate>
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
NSFontAttributeName: [UIFont systemFontOfSize:17]}]];
NSString *fingerprint = account.mxSession.crypto.deviceEd25519Key;
if (fingerprint)
{
fingerprint = [MXTools addWhiteSpacesToString:fingerprint every:4];
}
[cryptoInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:fingerprint ? fingerprint : @""
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
@ -1189,6 +1150,22 @@ SingleImagePickerPresenterDelegate>
}
}
- (void)setupDiscoverySection
{
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
NSArray<MXThirdPartyIdentifier*> *thirdPartyIdentifiers = account.threePIDs ?: @[];
SettingsDiscoveryViewModel *viewModel = [[SettingsDiscoveryViewModel alloc] initWithSession:self.mainSession thirdPartyIdentifiers:thirdPartyIdentifiers];
viewModel.coordinatorDelegate = self;
SettingsDiscoveryTableViewSection *discoverySection = [[SettingsDiscoveryTableViewSection alloc] initWithViewModel:viewModel];
discoverySection.delegate = self;
self.settingsDiscoveryViewModel = viewModel;
self.settingsDiscoveryTableViewSection = discoverySection;
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
@ -1224,10 +1201,6 @@ SingleImagePickerPresenterDelegate>
userSettingsProfilePictureIndex = 0;
userSettingsDisplayNameIndex = 1;
userSettingsChangePasswordIndex = 2;
userSettingsEmailStartIndex = 3;
userSettingsNewEmailIndex = userSettingsEmailStartIndex + account.linkedEmails.count;
userSettingsPhoneStartIndex = userSettingsNewEmailIndex + 1;
userSettingsNewPhoneIndex = userSettingsPhoneStartIndex + account.linkedPhoneNumbers.count;
// Hide some unsupported account settings
userSettingsFirstNameIndex = -1;
@ -1235,19 +1208,35 @@ SingleImagePickerPresenterDelegate>
userSettingsNightModeSepIndex = -1;
userSettingsNightModeIndex = -1;
count = userSettingsNewPhoneIndex + 1;
userSettingsEmailStartIndex = 3;
userSettingsNewEmailIndex = userSettingsEmailStartIndex + account.linkedEmails.count;
userSettingsPhoneStartIndex = userSettingsNewEmailIndex + 1;
userSettingsNewPhoneIndex = userSettingsPhoneStartIndex + account.linkedPhoneNumbers.count;
userSettingsThreePidsInformation = userSettingsNewPhoneIndex + 1;
count = userSettingsThreePidsInformation + 1;
}
else if (section == SETTINGS_SECTION_NOTIFICATIONS_SETTINGS_INDEX)
{
count = NOTIFICATION_SETTINGS_COUNT;
}
else if (section == SETTINGS_SECTION_CALLS_INDEX)
{
if ([MXCallKitAdapter callKitAvailable])
{
count = CALLS_COUNT;
if (!RiotSettings.shared.stunServerFallback)
{
count -= 2;
}
}
else if (section == SETTINGS_SECTION_DISCOVERY_INDEX)
{
count = self.settingsDiscoveryTableViewSection.numberOfRows;
}
else if (section == SETTINGS_SECTION_IDENTITY_SERVER_INDEX)
{
count = IDENTITY_SERVER_COUNT;
}
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
{
count = USER_INTERFACE_COUNT;
@ -1303,6 +1292,11 @@ SingleImagePickerPresenterDelegate>
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
{
count = devicesArray.count;
if (count)
{
// For some description (DEVICES_DESCRIPTION_INDEX)
count++;
}
}
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
{
@ -1389,6 +1383,7 @@ SingleImagePickerPresenterDelegate>
cell.textLabel.accessibilityIdentifier = nil;
cell.textLabel.font = [UIFont systemFontOfSize:17];
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.contentView.backgroundColor = UIColor.clearColor;
return cell;
}
@ -1610,9 +1605,7 @@ SingleImagePickerPresenterDelegate>
phoneCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_phone_number", @"Vector", nil);
NSString *e164 = [NSString stringWithFormat:@"+%@", account.linkedPhoneNumbers[row - userSettingsPhoneStartIndex]];
NBPhoneNumber *phoneNb = [[NBPhoneNumberUtil sharedInstance] parse:e164 defaultRegion:nil error:nil];
phoneCell.mxkTextField.text = [[NBPhoneNumberUtil sharedInstance] format:phoneNb numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:nil];
phoneCell.mxkTextField.text = [MXKTools readableMSISDN:account.linkedPhoneNumbers[row - userSettingsPhoneStartIndex]];
phoneCell.mxkTextField.userInteractionEnabled = NO;
cell = phoneCell;
@ -1695,6 +1688,21 @@ SingleImagePickerPresenterDelegate>
cell = newPhoneCell;
}
}
else if (row == userSettingsThreePidsInformation)
{
MXKTableViewCell *threePidsInformationCell = [self getDefaultTableViewCell:self.tableView];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_three_pids_management_information_part1", @"Vector", nil) attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor}];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_three_pids_management_information_part2", @"Vector", nil) attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"settings_three_pids_management_information_part3", @"Vector", nil) attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor}]];
threePidsInformationCell.textLabel.attributedText = attributedString;
threePidsInformationCell.textLabel.numberOfLines = 0;
threePidsInformationCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = threePidsInformationCell;
}
else if (row == userSettingsChangePasswordIndex)
{
MXKTableViewCellWithLabelAndTextField *passwordCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
@ -1800,17 +1808,101 @@ SingleImagePickerPresenterDelegate>
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleCallKit:) forControlEvents:UIControlEventTouchUpInside];
if (![MXCallKitAdapter callKitAvailable])
{
labelAndSwitchCell.mxkSwitch.on = NO;
labelAndSwitchCell.mxkSwitch.enabled = NO;
labelAndSwitchCell.mxkLabel.enabled = NO;
}
cell = labelAndSwitchCell;
}
else if (row == CALLS_DESCRIPTION_INDEX)
else if (row == CALLS_CALLKIT_DESCRIPTION_INDEX)
{
MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView];
globalInfoCell.textLabel.text = NSLocalizedStringFromTable(@"settings_callkit_info", @"Vector", nil);
globalInfoCell.textLabel.numberOfLines = 0;
globalInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
if (![MXCallKitAdapter callKitAvailable])
{
globalInfoCell.textLabel.enabled = NO;
}
cell = globalInfoCell;
}
else if (row == CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_calls_stun_server_fallback_button", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.allowStunServerFallback;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleStunServerFallback:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX)
{
NSString *stunFallbackHost = RiotSettings.shared.stunServerFallback;
// Remove "stun:"
stunFallbackHost = [stunFallbackHost componentsSeparatedByString:@":"].lastObject;
MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView];
globalInfoCell.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_calls_stun_server_fallback_description", @"Vector", nil), stunFallbackHost];
globalInfoCell.textLabel.numberOfLines = 0;
globalInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = globalInfoCell;
}
}
else if (section == SETTINGS_SECTION_DISCOVERY_INDEX)
{
cell = [self.settingsDiscoveryTableViewSection cellForRowAtRow:row];
}
else if (section == SETTINGS_SECTION_IDENTITY_SERVER_INDEX)
{
switch (row)
{
case IDENTITY_SERVER_INDEX:
{
MXKTableViewCell *isCell = [self getDefaultTableViewCell:tableView];
if (account.mxSession.identityService.identityServer)
{
isCell.textLabel.text = account.mxSession.identityService.identityServer;
}
else
{
isCell.textLabel.text = NSLocalizedStringFromTable(@"settings_identity_server_no_is", @"Vector", nil);
}
isCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell = isCell;
break;
}
case IDENTITY_SERVER_DESCRIPTION_INDEX:
{
MXKTableViewCell *descriptionCell = [self getDefaultTableViewCell:tableView];
if (account.mxSession.identityService.identityServer)
{
descriptionCell.textLabel.text = NSLocalizedStringFromTable(@"settings_identity_server_description", @"Vector", nil);
}
else
{
descriptionCell.textLabel.text = NSLocalizedStringFromTable(@"settings_identity_server_no_is_description", @"Vector", nil);
}
descriptionCell.textLabel.numberOfLines = 0;
descriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = descriptionCell;
break;
}
default:
break;
}
}
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
{
@ -2181,12 +2273,27 @@ SingleImagePickerPresenterDelegate>
}
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
{
MXKTableViewCell *deviceCell = [self getDefaultTableViewCell:tableView];
if (row < devicesArray.count)
if (row == DEVICES_DESCRIPTION_INDEX)
{
NSString *name = devicesArray[row].displayName;
NSString *deviceId = devicesArray[row].deviceId;
MXKTableViewCell *descriptionCell = [self getDefaultTableViewCell:tableView];
descriptionCell.textLabel.text = NSLocalizedStringFromTable(@"settings_devices_description", @"Vector", nil);
descriptionCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
descriptionCell.textLabel.font = [UIFont systemFontOfSize:15];
descriptionCell.textLabel.numberOfLines = 0;
descriptionCell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
descriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = descriptionCell;
}
else
{
NSUInteger deviceIndex = row - 1;
MXKTableViewCell *deviceCell = [self getDefaultTableViewCell:tableView];
if (deviceIndex < devicesArray.count)
{
NSString *name = devicesArray[deviceIndex].displayName;
NSString *deviceId = devicesArray[deviceIndex].deviceId;
deviceCell.textLabel.text = (name.length ? [NSString stringWithFormat:@"%@ (%@)", name, deviceId] : [NSString stringWithFormat:@"(%@)", deviceId]);
deviceCell.textLabel.numberOfLines = 0;
@ -2198,6 +2305,8 @@ SingleImagePickerPresenterDelegate>
cell = deviceCell;
}
}
else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX)
{
if (row == CRYPTOGRAPHY_INFO_INDEX)
@ -2291,11 +2400,16 @@ SingleImagePickerPresenterDelegate>
return NSLocalizedStringFromTable(@"settings_notifications_settings", @"Vector", nil);
}
else if (section == SETTINGS_SECTION_CALLS_INDEX)
{
if ([MXCallKitAdapter callKitAvailable])
{
return NSLocalizedStringFromTable(@"settings_calls_settings", @"Vector", nil);
}
else if (section == SETTINGS_SECTION_DISCOVERY_INDEX)
{
return NSLocalizedStringFromTable(@"settings_discovery_settings", @"Vector", nil);
}
else if (section == SETTINGS_SECTION_IDENTITY_SERVER_INDEX)
{
return NSLocalizedStringFromTable(@"settings_identity_server_settings", @"Vector", nil);
}
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
{
@ -2441,13 +2555,6 @@ SingleImagePickerPresenterDelegate>
}
}
}
else if (section == SETTINGS_SECTION_CALLS_INDEX)
{
if (![MXCallKitAdapter callKitAvailable])
{
return SECTION_TITLE_PADDING_WHEN_HIDDEN;
}
}
else if (section == SETTINGS_SECTION_FLAIR_INDEX)
{
if (groupsDataSource.joinedGroupsSection == -1)
@ -2473,13 +2580,6 @@ SingleImagePickerPresenterDelegate>
}
}
}
else if (section == SETTINGS_SECTION_CALLS_INDEX)
{
if (![MXCallKitAdapter callKitAvailable])
{
return SECTION_TITLE_PADDING_WHEN_HIDDEN;
}
}
else if (section == SETTINGS_SECTION_FLAIR_INDEX)
{
if (groupsDataSource.joinedGroupsSection == -1)
@ -2544,6 +2644,24 @@ SingleImagePickerPresenterDelegate>
[self showThemePicker];
}
}
else if (section == SETTINGS_SECTION_USER_SETTINGS_INDEX && row == userSettingsThreePidsInformation)
{
NSIndexPath *discoveryIndexPath = [NSIndexPath indexPathForRow:0 inSection:SETTINGS_SECTION_DISCOVERY_INDEX];
[tableView scrollToRowAtIndexPath:discoveryIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
else if (section == SETTINGS_SECTION_DISCOVERY_INDEX)
{
[self.settingsDiscoveryTableViewSection selectRow:indexPath.row];
}
else if (section == SETTINGS_SECTION_IDENTITY_SERVER_INDEX)
{
switch (row)
{
case IDENTITY_SERVER_INDEX:
[self showIdentityServerSettingsScreen];
break;
}
}
else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX)
{
MXSession* session = [AppDelegate theDelegate].mxSessions[0];
@ -2683,9 +2801,13 @@ SingleImagePickerPresenterDelegate>
}
else if (section == SETTINGS_SECTION_DEVICES_INDEX)
{
if (row < devicesArray.count)
if (row > DEVICES_DESCRIPTION_INDEX)
{
[self showDeviceDetails:devicesArray[row]];
NSUInteger deviceIndex = row - 1;
if (deviceIndex < devicesArray.count)
{
[self showDeviceDetails:devicesArray[deviceIndex]];
}
}
}
else if (section == SETTINGS_SECTION_CONTACTS_INDEX)
@ -2904,6 +3026,14 @@ SingleImagePickerPresenterDelegate>
[MXKAppSettings standardAppSettings].enableCallKit = switchButton.isOn;
}
- (void)toggleStunServerFallback:(id)sender
{
UISwitch *switchButton = (UISwitch*)sender;
RiotSettings.shared.allowStunServerFallback = switchButton.isOn;
self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? RiotSettings.shared.stunServerFallback : nil;
}
- (void)toggleShowDecodedContent:(id)sender
{
UISwitch *switchButton = (UISwitch*)sender;
@ -3444,7 +3574,7 @@ SingleImagePickerPresenterDelegate>
currentAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"abort"]
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
@ -3533,12 +3663,15 @@ SingleImagePickerPresenterDelegate>
MXSession* session = [AppDelegate theDelegate].mxSessions[0];
MXK3PID *new3PID = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:newEmailTextField.text];
[new3PID requestValidationTokenWithMatrixRestClient:session.matrixRestClient isDuringRegistration:NO nextLink:nil success:^{
[self showValidationEmailDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_email_validation_message"] for3PID:new3PID];
__block MX3PidAddSession *thirdPidAddSession;
thirdPidAddSession = [session.threePidAddManager startAddEmailSessionWithEmail:self->newEmailTextField.text nextLink:nil success:^{
} failure:^(NSError *error) {
[self showValidationEmailDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_email_validation_message"]
for3PidAddSession:thirdPidAddSession
threePidAddManager:session.threePidAddManager];
} failure:^(NSError * _Nonnull error) {
[self stopActivityIndicator];
@ -3546,7 +3679,9 @@ SingleImagePickerPresenterDelegate>
// Translate the potential MX error.
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse] || [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
if (mxError
&& ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse]
|| [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
{
NSMutableDictionary *userInfo;
if (error.userInfo)
@ -3573,6 +3708,15 @@ SingleImagePickerPresenterDelegate>
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
else if ([error.domain isEqualToString:MX3PidAddManagerErrorDomain]
&& error.code == MX3PidAddManagerErrorDomainIdentityServerRequired)
{
error = [NSError errorWithDomain:error.domain
code:error.code
userInfo:@{
NSLocalizedDescriptionKey: [NSBundle mxk_localizedStringForKey:@"auth_email_is_required"]
}];
}
// Notify user
NSString *myUserId = session.myUser.userId; // TODO: Hanlde multi-account
@ -3628,18 +3772,17 @@ SingleImagePickerPresenterDelegate>
NSString *msisdn;
if ([e164 hasPrefix:@"+"])
{
msisdn = [e164 substringFromIndex:1];
msisdn = e164;
}
else if ([e164 hasPrefix:@"00"])
{
msisdn = [e164 substringFromIndex:2];
msisdn = [NSString stringWithFormat:@"+%@", [e164 substringFromIndex:2]];
}
MXK3PID *new3PID = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumMSISDN andAddress:msisdn];
__block MX3PidAddSession *new3Pid;
new3Pid = [session.threePidAddManager startAddPhoneNumberSessionWithPhoneNumber:msisdn countryCode:nil success:^{
[new3PID requestValidationTokenWithMatrixRestClient:session.matrixRestClient isDuringRegistration:NO nextLink:nil success:^{
[self showValidationMsisdnDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_msisdn_validation_message"] for3PID:new3PID];
[self showValidationMsisdnDialogWithMessage:[NSBundle mxk_localizedStringForKey:@"account_msisdn_validation_message"] for3PidAddSession:new3Pid threePidAddManager:session.threePidAddManager];
} failure:^(NSError *error) {
@ -3649,7 +3792,9 @@ SingleImagePickerPresenterDelegate>
// Translate the potential MX error.
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse] || [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
if (mxError
&& ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse]
|| [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
{
NSMutableDictionary *userInfo;
if (error.userInfo)
@ -3676,11 +3821,19 @@ SingleImagePickerPresenterDelegate>
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
else if ([error.domain isEqualToString:MX3PidAddManagerErrorDomain]
&& error.code == MX3PidAddManagerErrorDomainIdentityServerRequired)
{
error = [NSError errorWithDomain:error.domain
code:error.code
userInfo:@{
NSLocalizedDescriptionKey: [NSBundle mxk_localizedStringForKey:@"auth_phone_is_required"]
}];
}
// Notify user
NSString *myUserId = session.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
@ -4420,4 +4573,105 @@ SingleImagePickerPresenterDelegate>
[self.tableView reloadData];
}
#pragma mark - Identity Server updates
- (void)registerAccountDataDidChangeIdentityServerNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAccountDataDidChangeIdentityServerNotification:) name:kMXSessionAccountDataDidChangeIdentityServerNotification object:nil];
}
- (void)handleAccountDataDidChangeIdentityServerNotification:(NSNotification*)notification
{
[self refreshSettings];
}
#pragma mark - SettingsDiscoveryTableViewSectionDelegate
- (void)settingsDiscoveryTableViewSectionDidUpdate:(SettingsDiscoveryTableViewSection *)settingsDiscoveryTableViewSection
{
[self.tableView reloadData];
}
- (MXKTableViewCell *)settingsDiscoveryTableViewSection:(SettingsDiscoveryTableViewSection *)settingsDiscoveryTableViewSection tableViewCellClass:(Class)tableViewCellClass forRow:(NSInteger)forRow
{
MXKTableViewCell *tableViewCell;
if ([tableViewCellClass isEqual:[MXKTableViewCell class]])
{
tableViewCell = [self getDefaultTableViewCell:self.tableView];
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithTextView class]])
{
tableViewCell = [self textViewCellForTableView:self.tableView atIndexPath:[NSIndexPath indexPathForRow:forRow inSection:SETTINGS_SECTION_DISCOVERY_INDEX]];
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithButton class]])
{
MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!cell)
{
cell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
cell.mxkButton.titleLabel.text = nil;
}
cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
tableViewCell = cell;
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithLabelAndSwitch class]])
{
tableViewCell = [self getLabelAndSwitchCell:self.tableView forIndexPath:[NSIndexPath indexPathForRow:forRow inSection:SETTINGS_SECTION_DISCOVERY_INDEX]];
}
return tableViewCell;
}
#pragma mark - SettingsDiscoveryViewModelCoordinatorDelegate
- (void)settingsDiscoveryViewModel:(SettingsDiscoveryViewModel *)viewModel didSelectThreePidWith:(NSString *)medium and:(NSString *)address
{
SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter *discoveryThreePidDetailsPresenter = [[SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession medium:medium adress:address];
MXWeakify(self);
[discoveryThreePidDetailsPresenter pushFrom:self.navigationController animated:YES popCompletion:^{
MXStrongifyAndReturnIfNil(self);
self.discoveryThreePidDetailsPresenter = nil;
}];
self.discoveryThreePidDetailsPresenter = discoveryThreePidDetailsPresenter;
}
- (void)settingsDiscoveryViewModelDidTapUserSettingsLink:(SettingsDiscoveryViewModel *)viewModel
{
NSIndexPath *discoveryIndexPath = [NSIndexPath indexPathForRow:userSettingsNewEmailIndex inSection:SETTINGS_SECTION_USER_SETTINGS_INDEX];
[self.tableView scrollToRowAtIndexPath:discoveryIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
#pragma mark - Identity Server
- (void)showIdentityServerSettingsScreen
{
identityServerSettingsCoordinatorBridgePresenter = [[SettingsIdentityServerCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
[identityServerSettingsCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES popCompletion:nil];
identityServerSettingsCoordinatorBridgePresenter.delegate = self;
}
#pragma mark - SettingsIdentityServerCoordinatorBridgePresenterDelegate
- (void)settingsIdentityServerCoordinatorBridgePresenterDelegateDidComplete:(SettingsIdentityServerCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
identityServerSettingsCoordinatorBridgePresenter = nil;
[self refreshSettings];
}
@end

View file

@ -19,14 +19,7 @@
/**
'StartChatViewController' instance is used to prepare new room creation.
*/
@interface StartChatViewController : ContactsTableViewController <UITableViewDataSource, UISearchBarDelegate, ContactsTableViewControllerDelegate>
@property (weak, nonatomic) IBOutlet UIView *searchBarHeader;
@property (weak, nonatomic) IBOutlet UISearchBar *searchBarView;
@property (weak, nonatomic) IBOutlet UIView *searchBarHeaderBorder;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *searchBarTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint;
@interface StartChatViewController : ContactsTableViewController
/**
Tell whether a search session is in progress

View file

@ -20,7 +20,7 @@
#import "AppDelegate.h"
#import "Riot-Swift.h"
@interface StartChatViewController ()
@interface StartChatViewController () <UITableViewDataSource, UISearchBarDelegate, ContactsTableViewControllerDelegate>
{
// The contact used to describe the current user.
MXKContact *userContact;
@ -42,6 +42,13 @@
NSMutableDictionary <NSString*, NSNumber*> *isMultiUseNameByDisplayName;
}
@property (weak, nonatomic) IBOutlet UIView *searchBarHeader;
@property (weak, nonatomic) IBOutlet UISearchBar *searchBarView;
@property (weak, nonatomic) IBOutlet UIView *searchBarHeaderBorder;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *searchBarTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint;
@end
@implementation StartChatViewController
@ -522,11 +529,7 @@
// The identity server must be defined
if (!self.mainSession.matrixRestClient.identityServer)
{
MXError *error = [[MXError alloc] initWithErrorCode:kMXSDKErrCodeStringMissingParameters error:@"No supplied identity server URL"];
NSLog(@"[StartChatViewController] Invite %@ failed", participantId);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:[error createNSError]];
continue;
}
@ -681,6 +684,38 @@
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact
{
// If contact has only an email the identity server must be defined
if (!self.mainSession.matrixRestClient.identityServer && contact.matrixIdentifiers.count == 0)
{
NSString *participantId;
if (contact.emailAddresses.count)
{
MXKEmail *email = contact.emailAddresses.firstObject;
participantId = email.emailAddress;
}
else
{
// This is the text filled by the user.
participantId = contact.displayName;
}
if ([MXTools isEmailAddress:participantId])
{
NSLog(@"[StartChatViewController] No identity server is configured, do not add participant with email");
[contactsTableViewController refreshCurrentSelectedCell:YES];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSBundle mxk_localizedStringForKey:@"error"]
message:NSLocalizedStringFromTable(@"room_creation_error_invite_user_by_email_without_identity_server", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
return;
}
}
if (contact)
{
// Update here the mutable list of participants

View file

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>

View file

@ -14,3 +14,4 @@
#import "EncryptionInfoView.h"
#import "EventFormatter.h"
#import "MediaPickerViewController.h"
#import "AppDelegate.h"

View file

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>CFBundleVersion</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>

View file

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>CFBundleVersion</key>
<string>0.9.5</string>
<string>0.10.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>