mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Finish v0.10.4
This commit is contained in:
commit
94d9e45d20
70 changed files with 3446 additions and 134 deletions
33
CHANGES.rst
33
CHANGES.rst
|
@ -1,9 +1,40 @@
|
|||
Changes in 0.10.4 (2019-12-11)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* ON/OFF Cross-signing development in a Lab setting (#2855).
|
||||
|
||||
Bug fix:
|
||||
* Device Verification: Stay in infinite waiting (#2878).
|
||||
|
||||
Changes in 0.10.3 (2019-12-05)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* Upgrade MatrixKit version ([v0.11.3](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.11.3)).
|
||||
* Integrations: Use the integrations manager provided by the homeserver admin via .well-known (#2815).
|
||||
* i18n: Add Welsh (cy).
|
||||
* i18n: Add Italian (it).
|
||||
* SerializationService: Add deserialisation of Any.
|
||||
* RiotSharedSettings: New class to handle user settings shared accross Riot apps.
|
||||
* Widgets: Check user permission before opening a widget (#2833).
|
||||
* Widgets: Check user permission before opening jitsi (#2842).
|
||||
* Widgets: Add a contextual menu to refresh, open outside, remove and revoke the permission (#2834).
|
||||
* Settings: Add an option for disabling use of the integration manager (#2843).
|
||||
* Jitsi: Display room name, user name and user avatar in the conference screen.
|
||||
* Improve UNNotificationSound compatibility with MA4 (IMA/ADPCM) file, thanks to @pixlwave (PR #2847).
|
||||
|
||||
Bug fix:
|
||||
* Accessibility: Make checkboxes accessible in terms of service screen.
|
||||
* RoomVC: Tapping on location links gives 'unable to open link' (#2803).
|
||||
* RoomVC: Reply to links fail with 'unable to open link' (#2804).
|
||||
|
||||
Changes in 0.10.2 (2019-11-15)
|
||||
===============================================
|
||||
|
||||
Bug fix:
|
||||
* Integrations: Fix terms consent display when they are required.
|
||||
|
||||
|
||||
Changes in 0.10.1 (2019-11-06)
|
||||
===============================================
|
||||
|
||||
|
|
4
Podfile
4
Podfile
|
@ -7,7 +7,7 @@ use_frameworks!
|
|||
|
||||
# Different flavours of pods to MatrixKit
|
||||
# The current MatrixKit pod version
|
||||
$matrixKitVersion = '0.11.2'
|
||||
$matrixKitVersion = '0.11.3'
|
||||
|
||||
# The develop branch version
|
||||
#$matrixKitVersion = 'develop'
|
||||
|
@ -64,7 +64,7 @@ abstract_target 'RiotPods' do
|
|||
pod 'SwiftUTI', :git => 'https://github.com/speramusinc/SwiftUTI.git', :branch => 'master'
|
||||
|
||||
# Piwik for analytics
|
||||
pod 'MatomoTracker', '~> 6.0.1'
|
||||
pod 'MatomoTracker', '~> 7.2.0'
|
||||
|
||||
# Remove warnings from "bad" pods
|
||||
pod 'OLMKit', :inhibit_warnings => true
|
||||
|
|
44
Podfile.lock
44
Podfile.lock
|
@ -46,44 +46,44 @@ PODS:
|
|||
- JitsiMeetSDK (2.3.1)
|
||||
- libbase58 (0.1.4)
|
||||
- libPhoneNumber-iOS (0.9.15)
|
||||
- MatomoTracker (6.0.1):
|
||||
- MatomoTracker/Core (= 6.0.1)
|
||||
- MatomoTracker/Core (6.0.1)
|
||||
- MatrixKit (0.11.2):
|
||||
- MatomoTracker (7.2.0):
|
||||
- MatomoTracker/Core (= 7.2.0)
|
||||
- MatomoTracker/Core (7.2.0)
|
||||
- MatrixKit (0.11.3):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.11.2)
|
||||
- MatrixSDK (= 0.15.0)
|
||||
- MatrixKit/Core (= 0.11.3)
|
||||
- MatrixSDK (= 0.15.2)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixKit/AppExtension (0.11.2):
|
||||
- MatrixKit/AppExtension (0.11.3):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- DTCoreText/Extension
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.15.0)
|
||||
- MatrixSDK (= 0.15.2)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixKit/Core (0.11.2):
|
||||
- MatrixKit/Core (0.11.3):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.15.0)
|
||||
- MatrixSDK (= 0.15.2)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixSDK (0.15.0):
|
||||
- MatrixSDK/Core (= 0.15.0)
|
||||
- MatrixSDK/Core (0.15.0):
|
||||
- MatrixSDK (0.15.2):
|
||||
- MatrixSDK/Core (= 0.15.2)
|
||||
- MatrixSDK/Core (0.15.2):
|
||||
- AFNetworking (~> 3.2.0)
|
||||
- GZIP (~> 1.2.2)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.1.0)
|
||||
- Realm (~> 3.17.3)
|
||||
- MatrixSDK/JingleCallStack (0.15.0):
|
||||
- MatrixSDK/JingleCallStack (0.15.2):
|
||||
- JitsiMeetSDK (~> 2.3.1)
|
||||
- MatrixSDK/Core
|
||||
- MatrixSDK/SwiftSupport (0.15.0):
|
||||
- MatrixSDK/SwiftSupport (0.15.2):
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.1.0):
|
||||
- OLMKit/olmc (= 3.1.0)
|
||||
|
@ -107,9 +107,9 @@ DEPENDENCIES:
|
|||
- cmark
|
||||
- DGCollectionViewLeftAlignFlowLayout (~> 1.0.4)
|
||||
- GBDeviceInfo (~> 6.3.0)
|
||||
- MatomoTracker (~> 6.0.1)
|
||||
- MatrixKit (= 0.11.2)
|
||||
- MatrixKit/AppExtension (= 0.11.2)
|
||||
- MatomoTracker (~> 7.2.0)
|
||||
- MatrixKit (= 0.11.3)
|
||||
- MatrixKit/AppExtension (= 0.11.3)
|
||||
- MatrixSDK/JingleCallStack
|
||||
- MatrixSDK/SwiftSupport
|
||||
- OLMKit
|
||||
|
@ -164,9 +164,9 @@ SPEC CHECKSUMS:
|
|||
JitsiMeetSDK: 69e4978fbab21f9a535d1bec3b8d43721a4c72b2
|
||||
libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd
|
||||
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
|
||||
MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5
|
||||
MatrixKit: 7f681c9086509a4f5e6e7172ce00f552cba1f8bc
|
||||
MatrixSDK: 342384d62bac5d95a31a7dab79e5f687bd87ddca
|
||||
MatomoTracker: 6f89e2561083685a360e223fb663e9ccd57c1d1a
|
||||
MatrixKit: 5a20025b05490a70694b701e607ec75e0988af21
|
||||
MatrixSDK: f83bd3c5519c1cb9ac3998f6423574cf528f0250
|
||||
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
|
||||
Realm: 5a1d9d47bfc101dd597668b1a8af4288a2557f6d
|
||||
Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b
|
||||
|
@ -175,6 +175,6 @@ SPEC CHECKSUMS:
|
|||
SwiftUTI: 917993c124f8eac25e88ced0202fc58d7eb50fa8
|
||||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
|
||||
PODFILE CHECKSUM: 32e1f5dcb15429b0ecff04da8ebce193f145edb5
|
||||
PODFILE CHECKSUM: 881048fb17d68dd834b18e23929482600daca7f3
|
||||
|
||||
COCOAPODS: 1.8.4
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; };
|
||||
3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; };
|
||||
3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; };
|
||||
32863A5A2384070300D07C4A /* RiotSharedSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32863A592384070300D07C4A /* RiotSharedSettings.swift */; };
|
||||
32863A5C2384074C00D07C4A /* RiotSettingAllowedWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32863A5B2384074C00D07C4A /* RiotSettingAllowedWidgets.swift */; };
|
||||
32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D692264CBA300C82226 /* SimpleScreenTemplateViewController.swift */; };
|
||||
32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D6A2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard */; };
|
||||
32891D702264DF7B00C82226 /* DeviceVerificationVerifiedViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D6E2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.storyboard */; };
|
||||
|
@ -113,6 +115,7 @@
|
|||
32F6B96C2270623100BBA352 /* DeviceVerificationDataLoadingCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9662270623100BBA352 /* DeviceVerificationDataLoadingCoordinatorType.swift */; };
|
||||
32F6B96D2270623100BBA352 /* DeviceVerificationDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9672270623100BBA352 /* DeviceVerificationDataLoadingViewModel.swift */; };
|
||||
32F6B96E2270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9682270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift */; };
|
||||
32FDC1CD2386CD390084717A /* RiotSettingIntegrationProvisioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FDC1CC2386CD390084717A /* RiotSettingIntegrationProvisioning.swift */; };
|
||||
3AF393339D2D566CE14AC200 /* Pods_RiotTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */; };
|
||||
405FD41D306133A48D9B5AA1 /* Pods_RiotPods_Riot.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1ACF09217ADF1D7E7A35BC02 /* Pods_RiotPods_Riot.framework */; };
|
||||
670966FEFE120D865FD8A5B6 /* Pods_RiotPods_SiriIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51187E952D5CECF6D6F5A28E /* Pods_RiotPods_SiriIntents.framework */; };
|
||||
|
@ -157,12 +160,15 @@
|
|||
B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B110872021F098EF003554A5 /* ActivityIndicatorView.xib */; };
|
||||
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872121F098EF003554A5 /* ActivityIndicatorPresenter.swift */; };
|
||||
B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B110872221F098F0003554A5 /* ActivityIndicatorView.swift */; };
|
||||
B11291EA238D35590077B478 /* SlidingModalPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11291E9238D35590077B478 /* SlidingModalPresentable.swift */; };
|
||||
B11291EC238D704C0077B478 /* FloatingPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11291EB238D704C0077B478 /* FloatingPoint.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 */; };
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.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 */; };
|
||||
|
@ -249,6 +255,12 @@
|
|||
B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; };
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; };
|
||||
B1A68593229E807A00D6C09A /* RoomBubbleCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */; };
|
||||
B1A6C10723881EF2002882FD /* SlidingModalPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6C10623881EF2002882FD /* SlidingModalPresenter.swift */; };
|
||||
B1A6C109238828A6002882FD /* SlidingModalPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6C108238828A6002882FD /* SlidingModalPresentationDelegate.swift */; };
|
||||
B1A6C10B23882B6C002882FD /* SlidingModalPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6C10A23882B6C002882FD /* SlidingModalPresentationAnimator.swift */; };
|
||||
B1A6C10D23882D1D002882FD /* SlidingModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6C10C23882D1D002882FD /* SlidingModalPresentationController.swift */; };
|
||||
B1A6C111238BD236002882FD /* SlidingModalContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6C110238BD236002882FD /* SlidingModalContainerView.swift */; };
|
||||
B1A6C113238BD245002882FD /* SlidingModalContainerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1A6C112238BD245002882FD /* SlidingModalContainerView.xib */; };
|
||||
B1B12B2922942315002CB419 /* UITouch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B12B2822942315002CB419 /* UITouch.swift */; };
|
||||
B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; };
|
||||
B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; };
|
||||
|
@ -501,6 +513,10 @@
|
|||
B1B9DEF122EB396B0065E677 /* ReactionHistoryViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEF022EB396B0065E677 /* ReactionHistoryViewData.swift */; };
|
||||
B1B9DEF422EB426D0065E677 /* ReactionHistoryViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEF222EB426D0065E677 /* ReactionHistoryViewCell.swift */; };
|
||||
B1B9DEF522EB426D0065E677 /* ReactionHistoryViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B9DEF322EB426D0065E677 /* ReactionHistoryViewCell.xib */; };
|
||||
B1BD71B5238DCBF700BA92E2 /* SlidingModalEmptyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BD71B4238DCBF700BA92E2 /* SlidingModalEmptyViewController.swift */; };
|
||||
B1BD71BC238E8F9600BA92E2 /* WidgetPermissionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BD71BA238E8F9600BA92E2 /* WidgetPermissionViewController.swift */; };
|
||||
B1BD71BF238EA56700BA92E2 /* WidgetPermissionViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1BD71BE238EA56700BA92E2 /* WidgetPermissionViewController.storyboard */; };
|
||||
B1BD71C1238EA92100BA92E2 /* WidgetPermissionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BD71C0238EA92000BA92E2 /* WidgetPermissionViewModel.swift */; };
|
||||
B1C335CD22F1C1320021BA8D /* CameraPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335CC22F1C1320021BA8D /* CameraPresenter.swift */; };
|
||||
B1C3360122F1ED600021BA8D /* MediaPickerCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335FE22F1ED5F0021BA8D /* MediaPickerCoordinatorType.swift */; };
|
||||
B1C3360222F1ED600021BA8D /* MediaPickerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */; };
|
||||
|
@ -579,7 +595,7 @@
|
|||
F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BB0D1E7009EC00A9B29C /* AppDelegate.m */; };
|
||||
F083BDE61E7009ED00A9B29C /* busy.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDB1E7009EC00A9B29C /* busy.mp3 */; };
|
||||
F083BDE71E7009ED00A9B29C /* callend.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDC1E7009EC00A9B29C /* callend.mp3 */; };
|
||||
F083BDE81E7009ED00A9B29C /* message.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDD1E7009EC00A9B29C /* message.mp3 */; };
|
||||
F083BDE81E7009ED00A9B29C /* message.caf in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDD1E7009EC00A9B29C /* message.caf */; };
|
||||
F083BDE91E7009ED00A9B29C /* ring.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDE1E7009EC00A9B29C /* ring.mp3 */; };
|
||||
F083BDEA1E7009ED00A9B29C /* ringback.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F083BBDF1E7009EC00A9B29C /* ringback.mp3 */; };
|
||||
F083BDED1E7009ED00A9B29C /* MXKRoomBubbleTableViewCell+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BBE61E7009EC00A9B29C /* MXKRoomBubbleTableViewCell+Riot.m */; };
|
||||
|
@ -696,6 +712,9 @@
|
|||
324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingViewModelType.swift; sourceTree = "<group>"; };
|
||||
324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinator.swift; sourceTree = "<group>"; };
|
||||
325789A5237AB241009388E6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
325789A6237AB27F009388E6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
325789A7237AB297009388E6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Vector.strings; sourceTree = "<group>"; };
|
||||
3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = "<group>"; };
|
||||
3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = "<group>"; };
|
||||
|
@ -703,6 +722,8 @@
|
|||
3275FD8B21A5A2C500B9C13D /* TermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = "<group>"; };
|
||||
3281BCF62201FA4200F4A383 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
|
||||
3284A35020A07C210044F922 /* postMessageAPI.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = postMessageAPI.js; sourceTree = "<group>"; };
|
||||
32863A592384070300D07C4A /* RiotSharedSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RiotSharedSettings.swift; sourceTree = "<group>"; };
|
||||
32863A5B2384074C00D07C4A /* RiotSettingAllowedWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiotSettingAllowedWidgets.swift; sourceTree = "<group>"; };
|
||||
32891D692264CBA300C82226 /* SimpleScreenTemplateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleScreenTemplateViewController.swift; sourceTree = "<group>"; };
|
||||
32891D6A2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SimpleScreenTemplateViewController.storyboard; sourceTree = "<group>"; };
|
||||
32891D6E2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationVerifiedViewController.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -758,6 +779,7 @@
|
|||
32F6B9662270623100BBA352 /* DeviceVerificationDataLoadingCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
32F6B9672270623100BBA352 /* DeviceVerificationDataLoadingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewModel.swift; sourceTree = "<group>"; };
|
||||
32F6B9682270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewModelType.swift; sourceTree = "<group>"; };
|
||||
32FDC1CC2386CD390084717A /* RiotSettingIntegrationProvisioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiotSettingIntegrationProvisioning.swift; sourceTree = "<group>"; };
|
||||
3942DD65EBEB7AE647C6392A /* Pods-RiotPods-SiriIntents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-SiriIntents.debug.xcconfig"; path = "Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3D78489021AC9E6400B98A7D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3D78489121AC9E6500B98A7D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
@ -815,12 +837,15 @@
|
|||
B110872021F098EF003554A5 /* ActivityIndicatorView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ActivityIndicatorView.xib; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
B11291E9238D35590077B478 /* SlidingModalPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalPresentable.swift; sourceTree = "<group>"; };
|
||||
B11291EB238D704C0077B478 /* FloatingPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPoint.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>"; };
|
||||
B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageURLParser.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>"; };
|
||||
|
@ -956,6 +981,12 @@
|
|||
B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
|
||||
B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomBubbleCellLayout.swift; sourceTree = "<group>"; };
|
||||
B1A6C10623881EF2002882FD /* SlidingModalPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalPresenter.swift; sourceTree = "<group>"; };
|
||||
B1A6C108238828A6002882FD /* SlidingModalPresentationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalPresentationDelegate.swift; sourceTree = "<group>"; };
|
||||
B1A6C10A23882B6C002882FD /* SlidingModalPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalPresentationAnimator.swift; sourceTree = "<group>"; };
|
||||
B1A6C10C23882D1D002882FD /* SlidingModalPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalPresentationController.swift; sourceTree = "<group>"; };
|
||||
B1A6C110238BD236002882FD /* SlidingModalContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalContainerView.swift; sourceTree = "<group>"; };
|
||||
B1A6C112238BD245002882FD /* SlidingModalContainerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SlidingModalContainerView.xib; sourceTree = "<group>"; };
|
||||
B1B12B2822942315002CB419 /* UITouch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITouch.swift; sourceTree = "<group>"; };
|
||||
B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = "<group>"; };
|
||||
B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = "<group>"; };
|
||||
|
@ -1344,6 +1375,10 @@
|
|||
B1B9DEF022EB396B0065E677 /* ReactionHistoryViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewData.swift; sourceTree = "<group>"; };
|
||||
B1B9DEF222EB426D0065E677 /* ReactionHistoryViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewCell.swift; sourceTree = "<group>"; };
|
||||
B1B9DEF322EB426D0065E677 /* ReactionHistoryViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionHistoryViewCell.xib; sourceTree = "<group>"; };
|
||||
B1BD71B4238DCBF700BA92E2 /* SlidingModalEmptyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlidingModalEmptyViewController.swift; sourceTree = "<group>"; };
|
||||
B1BD71BA238E8F9600BA92E2 /* WidgetPermissionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPermissionViewController.swift; sourceTree = "<group>"; };
|
||||
B1BD71BE238EA56700BA92E2 /* WidgetPermissionViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = WidgetPermissionViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1BD71C0238EA92000BA92E2 /* WidgetPermissionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPermissionViewModel.swift; sourceTree = "<group>"; };
|
||||
B1C335CC22F1C1320021BA8D /* CameraPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPresenter.swift; sourceTree = "<group>"; };
|
||||
B1C335FE22F1ED5F0021BA8D /* MediaPickerCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1369,6 +1404,9 @@
|
|||
B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = RoomContextualMenuViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextualMenuItemView.swift; sourceTree = "<group>"; };
|
||||
B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContextualMenuItemView.xib; sourceTree = "<group>"; };
|
||||
B1C6FFE723954CE70055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
B1C6FFE823954D3B0055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1C6FFE923954D4B0055347B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Vector.strings; sourceTree = "<group>"; };
|
||||
B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||
B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||
B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1426,7 +1464,7 @@
|
|||
F083BB0D1E7009EC00A9B29C /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
F083BBDB1E7009EC00A9B29C /* busy.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = busy.mp3; sourceTree = "<group>"; };
|
||||
F083BBDC1E7009EC00A9B29C /* callend.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = callend.mp3; sourceTree = "<group>"; };
|
||||
F083BBDD1E7009EC00A9B29C /* message.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = message.mp3; sourceTree = "<group>"; };
|
||||
F083BBDD1E7009EC00A9B29C /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = "<group>"; };
|
||||
F083BBDE1E7009EC00A9B29C /* ring.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ring.mp3; sourceTree = "<group>"; };
|
||||
F083BBDF1E7009EC00A9B29C /* ringback.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ringback.mp3; sourceTree = "<group>"; };
|
||||
F083BBE51E7009EC00A9B29C /* MXKRoomBubbleTableViewCell+Riot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MXKRoomBubbleTableViewCell+Riot.h"; sourceTree = "<group>"; };
|
||||
|
@ -1682,6 +1720,24 @@
|
|||
path = Incoming;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32863A572384070300D07C4A /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32863A582384070300D07C4A /* JSONModels */,
|
||||
32863A592384070300D07C4A /* RiotSharedSettings.swift */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32863A582384070300D07C4A /* JSONModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32863A5B2384074C00D07C4A /* RiotSettingAllowedWidgets.swift */,
|
||||
32FDC1CC2386CD390084717A /* RiotSettingIntegrationProvisioning.swift */,
|
||||
);
|
||||
path = JSONModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32891D682264C6A000C82226 /* SimpleScreenTemplate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1964,6 +2020,16 @@
|
|||
path = ActivityIndicator;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B11291ED238DC8C80077B478 /* WidgetPermission */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1BD71C0238EA92000BA92E2 /* WidgetPermissionViewModel.swift */,
|
||||
B1BD71BA238E8F9600BA92E2 /* WidgetPermissionViewController.swift */,
|
||||
B1BD71BE238EA56700BA92E2 /* WidgetPermissionViewController.storyboard */,
|
||||
);
|
||||
path = WidgetPermission;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B125FE19231D5B5600B72806 /* Discovery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1977,6 +2043,14 @@
|
|||
path = Discovery;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B12C56ED2396CB0100FAC6DE /* RoomMessageLinkParser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */,
|
||||
);
|
||||
path = RoomMessageLinkParser;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B14F142522144F6400FA0595 /* RecoveryKey */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2321,9 +2395,25 @@
|
|||
path = Riot/Modules/Common/CollectionView;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1A6C10623881EF2002882FD /* SlidingModalPresenter.swift */,
|
||||
B11291E9238D35590077B478 /* SlidingModalPresentable.swift */,
|
||||
B1A6C108238828A6002882FD /* SlidingModalPresentationDelegate.swift */,
|
||||
B1A6C10A23882B6C002882FD /* SlidingModalPresentationAnimator.swift */,
|
||||
B1A6C10C23882D1D002882FD /* SlidingModalPresentationController.swift */,
|
||||
B1A6C110238BD236002882FD /* SlidingModalContainerView.swift */,
|
||||
B1A6C112238BD245002882FD /* SlidingModalContainerView.xib */,
|
||||
B1BD71B4238DCBF700BA92E2 /* SlidingModalEmptyViewController.swift */,
|
||||
);
|
||||
path = SlidingModal;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B5567620EE6C4C00210D55 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1A6C10523881ECB002882FD /* SlidingModal */,
|
||||
32DB556722FDADE50016329E /* ServiceTerms */,
|
||||
3232AB94225730E100AD6A5C /* DeviceVerification */,
|
||||
B1B556EA20EE6C4C00210D55 /* Main */,
|
||||
|
@ -2824,6 +2914,7 @@
|
|||
children = (
|
||||
B1B5576420EE702800210D55 /* IntegrationManagerViewController.h */,
|
||||
B1B5576020EE702800210D55 /* IntegrationManagerViewController.m */,
|
||||
B11291ED238DC8C80077B478 /* WidgetPermission */,
|
||||
B1B5576120EE702800210D55 /* WidgetPicker */,
|
||||
B1B5576520EE702800210D55 /* Widgets */,
|
||||
);
|
||||
|
@ -3378,6 +3469,7 @@
|
|||
B1B5597C20EFC3DF00210D55 /* Managers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B12C56ED2396CB0100FAC6DE /* RoomMessageLinkParser */,
|
||||
B1B9DED822E9B7120065E677 /* Serialization */,
|
||||
B1FDF56321F68C0700BA3834 /* PasswordStrength */,
|
||||
B1798300211B137B001FD722 /* OnBoarding */,
|
||||
|
@ -3414,6 +3506,7 @@
|
|||
B1B5598A20EFC42100210D55 /* Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32863A572384070300D07C4A /* Shared */,
|
||||
B1B5597F20EFC3DF00210D55 /* RiotSettings.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
|
@ -3646,7 +3739,7 @@
|
|||
children = (
|
||||
F083BBDB1E7009EC00A9B29C /* busy.mp3 */,
|
||||
F083BBDC1E7009EC00A9B29C /* callend.mp3 */,
|
||||
F083BBDD1E7009EC00A9B29C /* message.mp3 */,
|
||||
F083BBDD1E7009EC00A9B29C /* message.caf */,
|
||||
F083BBDE1E7009EC00A9B29C /* ring.mp3 */,
|
||||
F083BBDF1E7009EC00A9B29C /* ringback.mp3 */,
|
||||
);
|
||||
|
@ -3684,6 +3777,7 @@
|
|||
B1C562CB228AB3510037F12A /* UIStackView.swift */,
|
||||
B1B12B2822942315002CB419 /* UITouch.swift */,
|
||||
B1DCC63322E72C1B00625807 /* UISearchBar.swift */,
|
||||
B11291EB238D704C0077B478 /* FloatingPoint.swift */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3913,6 +4007,8 @@
|
|||
ja,
|
||||
hu,
|
||||
pl,
|
||||
cy,
|
||||
it,
|
||||
);
|
||||
mainGroup = F094A9991B78D8F000B1FBBF;
|
||||
productRefGroup = F094A9A31B78D8F000B1FBBF /* Products */;
|
||||
|
@ -3962,6 +4058,7 @@
|
|||
B1B5592B20EF7A5D00210D55 /* TableViewCellWithButton.xib in Resources */,
|
||||
B1B5574620EE6C4D00210D55 /* StartChatViewController.xib in Resources */,
|
||||
B1B5590A20EF768F00210D55 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1A6C113238BD245002882FD /* SlidingModalContainerView.xib in Resources */,
|
||||
B1B5594420EF7BD000210D55 /* TableViewCellWithCollectionView.xib in Resources */,
|
||||
B1B558D520EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B1B5590020EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
|
@ -4015,7 +4112,7 @@
|
|||
B1B558EC20EF768F00210D55 /* RoomMembershipCollapsedBubbleCell.xib in Resources */,
|
||||
B1B558D720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B5590820EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
F083BDE81E7009ED00A9B29C /* message.mp3 in Resources */,
|
||||
F083BDE81E7009ED00A9B29C /* message.caf in Resources */,
|
||||
B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */,
|
||||
B1B9DEF522EB426D0065E677 /* ReactionHistoryViewCell.xib in Resources */,
|
||||
B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */,
|
||||
|
@ -4045,6 +4142,7 @@
|
|||
3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */,
|
||||
B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */,
|
||||
32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */,
|
||||
B1BD71BF238EA56700BA92E2 /* WidgetPermissionViewController.storyboard in Resources */,
|
||||
B1DCC63222E7026F00625807 /* EmojiPickerHeaderView.xib in Resources */,
|
||||
B1B5578E20EF568D00210D55 /* GroupInviteTableViewCell.xib in Resources */,
|
||||
B1B5582020EF625800210D55 /* SimpleRoomTitleView.xib in Resources */,
|
||||
|
@ -4345,6 +4443,7 @@
|
|||
B1B558DE20EF768F00210D55 /* RoomIncomingAttachmentBubbleCell.m in Sources */,
|
||||
B1B5574820EE6C4D00210D55 /* PeopleViewController.m in Sources */,
|
||||
B1B5598720EFC3E000210D55 /* Widget.m in Sources */,
|
||||
B1BD71C1238EA92100BA92E2 /* WidgetPermissionViewModel.swift in Sources */,
|
||||
B1B557E320EF60B900210D55 /* MessagesSearchResultAttachmentBubbleCell.m in Sources */,
|
||||
B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */,
|
||||
3232ABAA225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -4375,7 +4474,9 @@
|
|||
B1963B2E228F1C4900CBA17F /* BubbleReactionViewData.swift in Sources */,
|
||||
B1C3361C22F32B4A0021BA8D /* SingleImagePickerPresenter.swift in Sources */,
|
||||
B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */,
|
||||
B1BD71BC238E8F9600BA92E2 /* WidgetPermissionViewController.swift in Sources */,
|
||||
B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B11291EA238D35590077B478 /* SlidingModalPresentable.swift in Sources */,
|
||||
B157FAA823264BED00EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter.swift in Sources */,
|
||||
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
|
||||
|
@ -4396,6 +4497,7 @@
|
|||
32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */,
|
||||
B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */,
|
||||
B1A6C111238BD236002882FD /* SlidingModalContainerView.swift in Sources */,
|
||||
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */,
|
||||
3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */,
|
||||
B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
|
@ -4409,8 +4511,10 @@
|
|||
B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */,
|
||||
32F6B9692270623100BBA352 /* DeviceVerificationDataLoadingCoordinator.swift in Sources */,
|
||||
B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */,
|
||||
32863A5A2384070300D07C4A /* RiotSharedSettings.swift in Sources */,
|
||||
B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */,
|
||||
B10CFBC32268D99D00A5842E /* JitsiService.swift in Sources */,
|
||||
B1A6C10B23882B6C002882FD /* SlidingModalPresentationAnimator.swift in Sources */,
|
||||
B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B5573E20EE6C4D00210D55 /* RiotNavigationController.m in Sources */,
|
||||
B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */,
|
||||
|
@ -4433,6 +4537,7 @@
|
|||
3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */,
|
||||
F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */,
|
||||
B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */,
|
||||
B1A6C109238828A6002882FD /* SlidingModalPresentationDelegate.swift in Sources */,
|
||||
32DB557722FDADE50016329E /* ServiceTermsModalCoordinator.swift in Sources */,
|
||||
32DB557922FDADE50016329E /* ServiceTermsModalScreenViewModel.swift in Sources */,
|
||||
32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */,
|
||||
|
@ -4483,6 +4588,7 @@
|
|||
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */,
|
||||
B157FA9F23264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsCoordinator.swift in Sources */,
|
||||
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */,
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */,
|
||||
B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */,
|
||||
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
|
||||
B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */,
|
||||
|
@ -4510,6 +4616,7 @@
|
|||
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */,
|
||||
B1DCC61E22E5E17100625807 /* EmojiPickerViewModel.swift in Sources */,
|
||||
B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */,
|
||||
32863A5C2384074C00D07C4A /* RiotSettingAllowedWidgets.swift in Sources */,
|
||||
B1B9DEDA22E9B7350065E677 /* SerializationService.swift in Sources */,
|
||||
B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */,
|
||||
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */,
|
||||
|
@ -4539,6 +4646,7 @@
|
|||
B1B5572620EE6C4D00210D55 /* RoomFilesSearchViewController.m in Sources */,
|
||||
B1B5583120EF66BA00210D55 /* RoomIdOrAliasTableViewCell.m in Sources */,
|
||||
B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */,
|
||||
B1A6C10D23882D1D002882FD /* SlidingModalPresentationController.swift in Sources */,
|
||||
32A6001D22C661100042C1D9 /* EditHistoryCoordinatorType.swift in Sources */,
|
||||
F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */,
|
||||
B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */,
|
||||
|
@ -4552,6 +4660,7 @@
|
|||
B1B5574120EE6C4D00210D55 /* RecentsViewController.m in Sources */,
|
||||
B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */,
|
||||
B1B5577120EE702800210D55 /* StickerPickerViewController.m in Sources */,
|
||||
32FDC1CD2386CD390084717A /* RiotSettingIntegrationProvisioning.swift in Sources */,
|
||||
B104C2942203773C00D9F496 /* KeyBackupBannerPreferences.swift in Sources */,
|
||||
B1B5572020EE6C4D00210D55 /* ContactsTableViewController.m in Sources */,
|
||||
B1B5581920EF625800210D55 /* RoomTitleView.m in Sources */,
|
||||
|
@ -4566,6 +4675,7 @@
|
|||
B1B5597520EFB02A00210D55 /* InviteRecentTableViewCell.m in Sources */,
|
||||
B1B5571E20EE6C4D00210D55 /* ContactDetailsViewController.m in Sources */,
|
||||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */,
|
||||
B1A6C10723881EF2002882FD /* SlidingModalPresenter.swift in Sources */,
|
||||
B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */,
|
||||
B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */,
|
||||
B1DCC61A22E5E17100625807 /* EmojiPickerViewController.swift in Sources */,
|
||||
|
@ -4629,6 +4739,7 @@
|
|||
B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */,
|
||||
B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */,
|
||||
B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */,
|
||||
B11291EC238D704C0077B478 /* FloatingPoint.swift in Sources */,
|
||||
B1B557E920EF60F500210D55 /* MessagesSearchResultTextMsgBubbleCell.m in Sources */,
|
||||
324A2050225FC571004FE8B0 /* DeviceVerificationIncomingViewController.swift in Sources */,
|
||||
B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */,
|
||||
|
@ -4683,6 +4794,7 @@
|
|||
3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */,
|
||||
B1B9DEF122EB396B0065E677 /* ReactionHistoryViewData.swift in Sources */,
|
||||
B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
B1BD71B5238DCBF700BA92E2 /* SlidingModalEmptyViewController.swift in Sources */,
|
||||
B125FE1B231D5BF200B72806 /* SettingsDiscoveryTableViewSection.swift in Sources */,
|
||||
32242F0921E8B05F00725742 /* UIColor.swift in Sources */,
|
||||
B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */,
|
||||
|
@ -4756,6 +4868,8 @@
|
|||
3D78489221AC9E6500B98A7D /* ja */,
|
||||
3D78489521ACA25300B98A7D /* hu */,
|
||||
32DAF8DD231813E100654A44 /* pl */,
|
||||
325789A7237AB297009388E6 /* cy */,
|
||||
B1C6FFE923954D4B0055347B /* it */,
|
||||
);
|
||||
name = Vector.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4779,6 +4893,8 @@
|
|||
3D78489021AC9E6400B98A7D /* ja */,
|
||||
3D78489321ACA25200B98A7D /* hu */,
|
||||
32DAF8DB231813C800654A44 /* pl */,
|
||||
325789A5237AB241009388E6 /* cy */,
|
||||
B1C6FFE723954CE70055347B /* it */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4802,6 +4918,8 @@
|
|||
3D78489121AC9E6500B98A7D /* ja */,
|
||||
3D78489421ACA25300B98A7D /* hu */,
|
||||
32DAF8DC231813D500654A44 /* pl */,
|
||||
325789A6237AB27F009388E6 /* cy */,
|
||||
B1C6FFE823954D3B0055347B /* it */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4829,6 +4947,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9C3242E3FE95BCDA9562C75D /* Pods-RiotPods-RiotShareExtension.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
|
@ -4868,6 +4987,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 4FC6A5D63FAD1B27C2F57AFA /* Pods-RiotPods-RiotShareExtension.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
|
@ -4908,6 +5028,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 3942DD65EBEB7AE647C6392A /* Pods-RiotPods-SiriIntents.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
|
@ -4940,6 +5061,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = E2599D0ECB8DD206624E450B /* Pods-RiotPods-SiriIntents.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
|
@ -5079,6 +5201,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 43C2962BE367F59220F517FA /* Pods-RiotPods-Riot.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Riot/SupportingFiles/Riot.entitlements;
|
||||
|
@ -5112,6 +5235,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B43DC75D1590BB8A4243BD4D /* Pods-RiotPods-Riot.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Riot/SupportingFiles/Riot.entitlements;
|
||||
|
@ -5143,7 +5267,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
DEVELOPMENT_TEAM = 7J4U792NQT;
|
||||
|
@ -5174,7 +5298,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AC34BF67FD21A9D01C16AE5D /* Pods-RiotTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
DEVELOPMENT_TEAM = 7J4U792NQT;
|
||||
|
|
|
@ -238,6 +238,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
@property (weak, nonatomic) UIViewController *gdprConsentController;
|
||||
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) SlidingModalPresenter *slidingModalPresenter;
|
||||
|
||||
/**
|
||||
Used to manage on boarding steps, like create DM with riot bot
|
||||
|
@ -374,7 +375,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
|
||||
{
|
||||
// Create message sound
|
||||
NSURL *messageSoundURL = [[NSBundle mainBundle] URLForResource:@"message" withExtension:@"mp3"];
|
||||
NSURL *messageSoundURL = [[NSBundle mainBundle] URLForResource:@"message" withExtension:@"caf"];
|
||||
AudioServicesCreateSystemSoundID((__bridge CFURLRef)messageSoundURL, &_messageSound);
|
||||
|
||||
NSLog(@"[AppDelegate] willFinishLaunchingWithOptions: Done");
|
||||
|
@ -1565,7 +1566,7 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
soundName = action.parameters[@"value"];
|
||||
if ([soundName isEqualToString:@"default"])
|
||||
{
|
||||
soundName = @"message.mp3";
|
||||
soundName = @"message.caf";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4099,18 +4100,27 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
{
|
||||
if (!_jitsiViewController && !currentCallViewController)
|
||||
{
|
||||
_jitsiViewController = [JitsiViewController jitsiViewController];
|
||||
MXWeakify(self);
|
||||
[self checkPermissionForNativeWidget:jitsiWidget fromUrl:JitsiService.shared.serverURL completion:^(BOOL granted) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
if (!granted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[_jitsiViewController openWidget:jitsiWidget withVideo:video success:^{
|
||||
self->_jitsiViewController = [JitsiViewController jitsiViewController];
|
||||
|
||||
_jitsiViewController.delegate = self;
|
||||
[self presentJitsiViewController:nil];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
[self->_jitsiViewController openWidget:jitsiWidget withVideo:video success:^{
|
||||
|
||||
_jitsiViewController = nil;
|
||||
self->_jitsiViewController.delegate = self;
|
||||
[self presentJitsiViewController:nil];
|
||||
|
||||
[self showAlertWithTitle:nil message:NSLocalizedStringFromTable(@"call_jitsi_error", @"Vector", nil)];
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
self->_jitsiViewController = nil;
|
||||
|
||||
[self showAlertWithTitle:nil message:NSLocalizedStringFromTable(@"call_jitsi_error", @"Vector", nil)];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
else
|
||||
|
@ -4166,6 +4176,123 @@ NSString *const AppDelegateDidValidateEmailNotificationClientSecretKey = @"AppDe
|
|||
}
|
||||
|
||||
|
||||
#pragma mark - Native Widget Permission
|
||||
|
||||
- (void)checkPermissionForNativeWidget:(Widget*)widget fromUrl:(NSURL*)url completion:(void (^)(BOOL granted))completion
|
||||
{
|
||||
MXSession *session = widget.mxSession;
|
||||
|
||||
if ([widget.widgetEvent.sender isEqualToString:session.myUser.userId])
|
||||
{
|
||||
// No need of more permission check if the user created the widget
|
||||
completion(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permission in user Riot settings
|
||||
__block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
|
||||
WidgetPermission permission = [sharedSettings permissionForNative:widget fromUrl:url];
|
||||
if (permission == WidgetPermissionGranted)
|
||||
{
|
||||
completion(YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: ask permission again if the user previously declined it
|
||||
[self askNativeWidgetPermissionWithWidget:widget completion:^(BOOL granted) {
|
||||
// Update the settings in user account data in parallel
|
||||
[sharedSettings setPermission:granted ? WidgetPermissionGranted : WidgetPermissionDeclined
|
||||
forNative:widget fromUrl:url
|
||||
success:^
|
||||
{
|
||||
sharedSettings = nil;
|
||||
}
|
||||
failure:^(NSError * _Nullable error)
|
||||
{
|
||||
NSLog(@"[WidgetVC] setPermissionForWidget failed. Error: %@", error);
|
||||
sharedSettings = nil;
|
||||
}];
|
||||
|
||||
completion(granted);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)askNativeWidgetPermissionWithWidget:(Widget*)widget completion:(void (^)(BOOL granted))completion
|
||||
{
|
||||
if (!self.slidingModalPresenter)
|
||||
{
|
||||
self.slidingModalPresenter = [SlidingModalPresenter new];
|
||||
}
|
||||
|
||||
[self.slidingModalPresenter dismissWithAnimated:NO completion:nil];
|
||||
|
||||
NSString *widgetCreatorUserId = widget.widgetEvent.sender ?: NSLocalizedStringFromTable(@"room_participants_unknown", @"Vector", nil);
|
||||
|
||||
MXSession *session = widget.mxSession;
|
||||
MXRoom *room = [session roomWithRoomId:widget.widgetEvent.roomId];
|
||||
MXRoomState *roomState = room.dangerousSyncState;
|
||||
MXRoomMember *widgetCreatorRoomMember = [roomState.members memberWithUserId:widgetCreatorUserId];
|
||||
|
||||
NSString *widgetDomain = @"";
|
||||
|
||||
if (widget.url)
|
||||
{
|
||||
NSString *host = [[NSURL alloc] initWithString:widget.url].host;
|
||||
if (host)
|
||||
{
|
||||
widgetDomain = host;
|
||||
}
|
||||
}
|
||||
|
||||
MXMediaManager *mediaManager = widget.mxSession.mediaManager;
|
||||
NSString *widgetCreatorDisplayName = widgetCreatorRoomMember.displayname;
|
||||
NSString *widgetCreatorAvatarURL = widgetCreatorRoomMember.avatarUrl;
|
||||
|
||||
NSArray<NSString*> *permissionStrings = @[
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_display_name_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_avatar_url_permission", @"Vector", nil)
|
||||
];
|
||||
|
||||
WidgetPermissionViewModel *widgetPermissionViewModel = [[WidgetPermissionViewModel alloc] initWithCreatorUserId:widgetCreatorUserId
|
||||
creatorDisplayName:widgetCreatorDisplayName creatorAvatarUrl:widgetCreatorAvatarURL widgetDomain:widgetDomain
|
||||
isWebviewWidget:NO
|
||||
widgetPermissions:permissionStrings
|
||||
mediaManager:mediaManager];
|
||||
|
||||
|
||||
WidgetPermissionViewController *widgetPermissionViewController = [WidgetPermissionViewController instantiateWith:widgetPermissionViewModel];
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
widgetPermissionViewController.didTapContinueButton = ^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self.slidingModalPresenter dismissWithAnimated:YES completion:^{
|
||||
completion(YES);
|
||||
}];
|
||||
};
|
||||
|
||||
widgetPermissionViewController.didTapCloseButton = ^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self.slidingModalPresenter dismissWithAnimated:YES completion:^{
|
||||
completion(NO);
|
||||
}];
|
||||
};
|
||||
|
||||
UIViewController *presentingViewController = self.window.rootViewController.presentedViewController ?: self.window.rootViewController;
|
||||
|
||||
[self.slidingModalPresenter present:widgetPermissionViewController
|
||||
from:presentingViewController
|
||||
animated:YES
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Call status handling
|
||||
|
||||
- (void)addCallStatusBar:(NSString*)buttonTitle
|
||||
|
|
23
Riot/Assets/Images.xcassets/Common/close_button.imageset/Contents.json
vendored
Normal file
23
Riot/Assets/Images.xcassets/Common/close_button.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "close_button.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "close_button@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "close_button@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button@2x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button@3x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/close_button.imageset/close_button@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
Riot/Assets/Sounds/message.caf
Normal file
BIN
Riot/Assets/Sounds/message.caf
Normal file
Binary file not shown.
Binary file not shown.
|
@ -825,8 +825,8 @@
|
|||
"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_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" = "Сигурни ли сте, че искате да оттеглите тази покана?";
|
||||
|
@ -867,3 +867,59 @@
|
|||
"settings_devices_description" = "Публичното име на устройството е видимо за хората, с които общувате";
|
||||
"settings_discovery_no_identity_server" = "В момента не използвате сървър за самоличност. Добавете такъв, за да бъдете откриваеми от съществуващи познати контакти.";
|
||||
"settings_discovery_terms_not_signed" = "Съгласете се с условията за ползване на сървъра за самоличност (%@) за да бъдете откриваеми по имейл адрес или телефон.";
|
||||
"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" = "Използвайки сървъра за самоличност по-горе, може да откривате и да бъдете открити от съществуващи познати ваши контакти.";
|
||||
"settings_identity_server_no_is" = "Не е настроен сървър за самоличност";
|
||||
"settings_identity_server_no_is_description" = "В момента не използвате сървър за самоличност. Добавете такъв по-горе, за да откривате и бъдете откриваеми от съществуващи ваши контакти.";
|
||||
// Identity server settings
|
||||
"identity_server_settings_title" = "Сървър за самоличност";
|
||||
"identity_server_settings_description" = "В момента използвате %2 за да откривате и да бъдете открити от съществуващи ваши контакти.";
|
||||
"identity_server_settings_no_is_description" = "В момента не използвате сървър за самоличност. Добавете такъв по-горе, за да откривате и бъдете открити от съществуващи ваши контакти.";
|
||||
"identity_server_settings_place_holder" = "Въведете сървър за самоличност";
|
||||
"identity_server_settings_add" = "Добави";
|
||||
"identity_server_settings_change" = "Промени";
|
||||
"identity_server_settings_disconnect_info" = "Ако прекъснете връзката със сървъра за самоличност, няма да можете да бъдете открити от други потребители, както и да ви канят в стаи по имейл или телефонен номер.";
|
||||
"identity_server_settings_disconnect" = "Прекъсни";
|
||||
"identity_server_settings_alert_no_terms_title" = "Сървъра за самоличност няма условия за ползване";
|
||||
"identity_server_settings_alert_no_terms" = "Сървъра за самоличност, който сте избрали няма условия за ползване на услугата. Продължете, само ако вярвате на собственика на сървъра.";
|
||||
"identity_server_settings_alert_change_title" = "Промяна на сървър за самоличност";
|
||||
"identity_server_settings_alert_change" = "Прекъсване от сървър за самоличност %1$@ и вместо това свързване с %2$@?";
|
||||
"identity_server_settings_alert_disconnect_title" = "Прекъсване на връзка със сървър за самоличност";
|
||||
"identity_server_settings_alert_disconnect" = "Прекъсване на връзката със сървър за самоличност %@?";
|
||||
"identity_server_settings_alert_disconnect_button" = "Прекъсни";
|
||||
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "Личните ви данни все още са споделени със сървър за самоличност %@.\n\nПрепоръчваме да премахнете имейл адресите и телефонните номера от сървъра за самоличност преди прекъсване на връзката.";
|
||||
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Прекъсни въпреки това";
|
||||
"identity_server_settings_alert_error_terms_not_accepted" = "Трябва да приемете условията на %@ за да го използвате за сървър за самоличност.";
|
||||
"identity_server_settings_alert_error_invalid_identity_server" = "%@ не е валиден сървър за самоличност.";
|
||||
"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" = "Опитай с %@";
|
||||
"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" = "Приемете условията на сървъра за самоличност (%@) за да откривате контакти.";
|
||||
// Generic errors
|
||||
"error_invite_3pid_with_no_identity_server" = "Добавете сървър за самоличност в настройки за да каните по имейл.";
|
||||
"error_not_supported_on_mobile" = "Не може да правите това от %@ мобилен телефон.";
|
||||
"settings_integrations" = "ИНТЕГРАЦИИ";
|
||||
"settings_integrations_allow_button" = "Управлявай интеграциите";
|
||||
"settings_integrations_allow_description" = "Използвайте мениджър на интеграции (%@) за да управлявате ботове, мостове към други мрежи, приспособления и стикери.\n\nМениджърите на интеграции получават данни за конфигурация, могат да модифицират приспособления, да пращат покани в стаи и да контролират нивата на достъп вместо вас.";
|
||||
"widget_menu_refresh" = "Опресни";
|
||||
"widget_menu_open_outside" = "Отвори в браузър";
|
||||
"widget_menu_revoke_permission" = "Премахни достъпа за мен";
|
||||
"widget_menu_remove" = "Премахни за всички";
|
||||
"widget_integration_manager_disabled" = "Необходимо е да включите мениджър на интеграции от настройки";
|
||||
|
|
6
Riot/Assets/cy.lproj/InfoPlist.strings
Normal file
6
Riot/Assets/cy.lproj/InfoPlist.strings
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Permissions usage explanations
|
||||
"NSCameraUsageDescription" = "Defnyddir y camera i dynnu lluniau a fideos, gwneud galwadau fideo.";
|
||||
"NSPhotoLibraryUsageDescription" = "Defnyddir y llyfrgell ffotograffau i anfon lluniau a fideos.";
|
||||
"NSMicrophoneUsageDescription" = "Defnyddir y meicroffon i gymryd fideos, gwneud galwadau.";
|
||||
"NSContactsUsageDescription" = "I ddarganfod cysylltiadau sydd eisoes yn defnyddio Matrix, gall Riot anfon cyfeiriadau e-bost a rhifau ffôn yn eich llyfr cyfeiriadau at y gweinydd adnabod Matrix o'ch dewis. Pan gânt eu cefnogi, mae data personol yn cael ei amgodio cyn ei anfon - gwiriwch bolisi preifatrwydd eich gweinydd adnabod i gael mwy o fanylion.";
|
||||
"NSCalendarsUsageDescription" = "Gweler eich cyfarfodydd a drefnwyd yn yr ap.";
|
56
Riot/Assets/cy.lproj/Localizable.strings
Normal file
56
Riot/Assets/cy.lproj/Localizable.strings
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* Message title for a specific person in a named room */
|
||||
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ yn %@";
|
||||
/* New message from a specific person, not referencing a room */
|
||||
"MSG_FROM_USER" = "Anfonwyd %@ neges";
|
||||
/* New message from a specific person in a named room */
|
||||
"MSG_FROM_USER_IN_ROOM" = "Postiodd %@ yn %@";
|
||||
/* 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" = "%@ yn %@: %@";
|
||||
/* 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" = "Anfonwyd %@ lun %@";
|
||||
/* New action message from a specific person in a named room. */
|
||||
"IMAGE_FROM_USER_IN_ROOM" = "Postiodd %@ lun %@ yn %@";
|
||||
/* A single unread message in a room */
|
||||
"SINGLE_UNREAD_IN_ROOM" = "Cawsoch neges yn %@";
|
||||
/* A single unread message */
|
||||
"SINGLE_UNREAD" = "Cawsoch neges";
|
||||
/* Sticker from a specific person, not referencing a room. */
|
||||
"STICKER_FROM_USER" = "Anfonodd %@ sticer";
|
||||
/* Multiple unread messages in a room */
|
||||
"UNREAD_IN_ROOM" = "%@ neges newydd yn %@";
|
||||
/* Multiple unread messages from a specific person, not referencing a room */
|
||||
"MSGS_FROM_USER" = "%@ neges newydd yn %@";
|
||||
/* Multiple unread messages from two people */
|
||||
"MSGS_FROM_TWO_USERS" = "%@ neges newydd gan %@ a %@";
|
||||
/* Multiple unread messages from three people */
|
||||
"MSGS_FROM_THREE_USERS" = "%@ neges newydd gan %@, %@ a %@";
|
||||
/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */
|
||||
"MSGS_FROM_TWO_PLUS_USERS" = "%@ neges newydd gan %@, %@ ac eraill";
|
||||
/* Multiple messages in two rooms */
|
||||
"MSGS_IN_TWO_ROOMS" = "%@ neges newydd yn %@ a %@";
|
||||
/* Look, stuff's happened, alright? Just open the app. */
|
||||
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ neges newydd yn %@, %@ ac eraill";
|
||||
/* A user has invited you to a chat */
|
||||
"USER_INVITE_TO_CHAT" = "Mae %@ wedi eich gwahodd chi i sgwrsio";
|
||||
/* A user has invited you to an (unamed) group chat */
|
||||
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "Mae %@ wedi eich gwahodd chi i sgwrsio mewn grŵp";
|
||||
/* A user has invited you to a named room */
|
||||
"USER_INVITE_TO_NAMED_ROOM" = "Mae %@ wedi eich gwahodd chi i %@";
|
||||
/* Incoming one-to-one voice call */
|
||||
"VOICE_CALL_FROM_USER" = "Galwad gan %@";
|
||||
/* Incoming one-to-one video call */
|
||||
"VIDEO_CALL_FROM_USER" = "Galwad fideo gan %@";
|
||||
/* Incoming unnamed voice conference invite from a specific person */
|
||||
"VOICE_CONF_FROM_USER" = "Galwad grŵp gan %@";
|
||||
/* Incoming unnamed video conference invite from a specific person */
|
||||
"VIDEO_CONF_FROM_USER" = "Galwad fideo grŵp gan %@";
|
||||
/* Incoming named voice conference invite from a specific person */
|
||||
"VOICE_CONF_NAMED_FROM_USER" = "Galwad grŵp gan %@: '%@'";
|
||||
/* Incoming named video conference invite from a specific person */
|
||||
"VIDEO_CONF_NAMED_FROM_USER" = "Galwad fideo grŵp gan %@: '%@'";
|
|
@ -172,7 +172,7 @@
|
|||
// Contacts
|
||||
"contacts_address_book_section" = "CYSYLLTIADAU LLEOL";
|
||||
"contacts_address_book_matrix_users_toggle" = "Defnyddwyr Matrix yn unig";
|
||||
"contacts_address_book_no_identity_server" = "Dim gweinyddwr adnabod wedi'i osod";
|
||||
"contacts_address_book_no_identity_server" = "Dim gweinydd adnabod wedi'i osod";
|
||||
"contacts_address_book_no_contact" = "Dim cysylltiadau lleol";
|
||||
"contacts_address_book_permission_required" = "Mae angen caniatâd i gael mynediad at gysylltiadau lleol";
|
||||
"contacts_address_book_permission_denied" = "Gwrthodwyd caniatâd i Riot gael mynediad i'ch cysylltiadau lleol";
|
||||
|
@ -425,23 +425,23 @@
|
|||
"settings_key_backup_info" = "Sicrheir negeseuon wedi'u hamgryptio gydag amgryptio o'r dechrau i'r diwedd. Dim ond chi a'r derbynnydd / derbynwyr sydd â'r allweddi i ddarllen y negeseuon hyn.";
|
||||
"settings_key_backup_info_checking" = "Gwirio...";
|
||||
"settings_key_backup_info_none" = "Nid yw'ch allweddi yn cael eu cadw wrth gefn o'r ddyfais hon.";
|
||||
"settings_key_backup_info_signout_warning" = "Cysylltwch y ddyfais hon â Key Backup cyn arwyddo allan er mwyn osgoi colli unrhyw allweddi a allai fod ar y ddyfais hon yn unig.";
|
||||
"settings_key_backup_info_version" = "Fersiwn Key Backup: %@";
|
||||
"settings_key_backup_info_signout_warning" = "Cysylltwch y ddyfais hon â Allweddi Wrth Gefn cyn arwyddo allan er mwyn osgoi colli unrhyw allweddi a allai fod ar y ddyfais hon yn unig.";
|
||||
"settings_key_backup_info_version" = "Fersiwn Allweddi Wrth Gefn: %@";
|
||||
"settings_key_backup_info_algorithm" = "Algorithm: %@";
|
||||
"settings_key_backup_info_valid" = "Mae'r ddyfais hon yn gwneud copi wrth gefn o'ch allweddi.";
|
||||
"settings_key_backup_info_not_valid" = "Nid yw'r ddyfais hon yn gwneud copi wrth gefn o'ch allweddi, ond mae gennych gopi wrth gefn y gallwch ei adfer ac ychwanegu ato wrth symud ymlaen.";
|
||||
"settings_key_backup_info_progress" = "Creu copi wrth gefn o allweddi %@...";
|
||||
"settings_key_backup_info_progress_done" = "Pob allwedd â copi wrth gefn";
|
||||
"settings_key_backup_info_trust_signature_unknown" = "Mae gan Key Backup lofnod o'r ddyfais gydag ID: %@";
|
||||
"settings_key_backup_info_trust_signature_valid" = "Mae gan Key Backup lofnod dilys o'r ddyfais hon";
|
||||
"settings_key_backup_info_trust_signature_valid_device_verified" = "Mae gan Key Backup lofnod dilys o %@";
|
||||
"settings_key_backup_info_trust_signature_valid_device_unverified" = "Mae gan Key Backup lofnod o %@";
|
||||
"settings_key_backup_info_trust_signature_invalid_device_verified" = "Mae gan Key Backup lofnod annilys o %@";
|
||||
"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Mae gan Key Backup lofnod annilys o %@";
|
||||
"settings_key_backup_button_create" = "Dechrau defnyddio Key Backup";
|
||||
"settings_key_backup_info_trust_signature_unknown" = "Mae gan Allweddi Wrth Gefn lofnod o'r ddyfais gydag ID: %@";
|
||||
"settings_key_backup_info_trust_signature_valid" = "Mae gan Allweddi Wrth Gefn lofnod dilys o'r ddyfais hon";
|
||||
"settings_key_backup_info_trust_signature_valid_device_verified" = "Mae gan Allweddi Wrth Gefn lofnod dilys o %@";
|
||||
"settings_key_backup_info_trust_signature_valid_device_unverified" = "Mae gan Allweddi Wrth Gefn lofnod o %@";
|
||||
"settings_key_backup_info_trust_signature_invalid_device_verified" = "Mae gan Allweddi Wrth Gefn lofnod annilys o %@";
|
||||
"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Mae gan Allweddi Wrth Gefn lofnod annilys o %@";
|
||||
"settings_key_backup_button_create" = "Dechrau defnyddio Allweddi Wrth Gefn";
|
||||
"settings_key_backup_button_restore" = "Adfer o'r copi wrth gefn";
|
||||
"settings_key_backup_button_delete" = "Dileu copi wrth gefn";
|
||||
"settings_key_backup_button_connect" = "Cysylltwch y ddyfais hon i Key Backup";
|
||||
"settings_key_backup_button_connect" = "Cysylltwch y ddyfais hon i Allweddi Wrth Gefn";
|
||||
"settings_key_backup_delete_confirmation_prompt_title" = "Dileu copi wrth gefn";
|
||||
"settings_key_backup_delete_confirmation_prompt_msg" = "Ydych chi'n siwr? Byddwch yn colli'ch negeseuon wedi'u hamgryptio os nad yw'ch allweddi wedi'u cadw wrth gefn yn gywir.";
|
||||
"settings_devices_description" = "Mae enw cyhoeddus dyfais yn weladwy i'r bobl rydych chi'n cyfathrebu â nhw";
|
||||
|
@ -460,7 +460,7 @@
|
|||
"settings_discovery_three_pid_details_cancel_email_validation_action" = "Canslo gwiro e-bost";
|
||||
"settings_discovery_three_pid_details_enter_sms_code_action" = "Rhowch côd actifadu neges destyn";
|
||||
"settings_identity_server_description" = "Gan ddefnyddio'r gweinydd adnabod a osodir uchod, gallwch ddarganfod a bod yn ddarganfyddadwy gan gysylltiadau presennol rydych chi'n eu hadnabod.";
|
||||
"settings_identity_server_no_is" = "Dim gweinyddwr adnabod wedi'i osod";
|
||||
"settings_identity_server_no_is" = "Dim gweinydd adnabod wedi'i osod";
|
||||
"settings_identity_server_no_is_description" = "Ar hyn o bryd nid ydych yn defnyddio gweinydd adnabod. I ddarganfod a bod yn ddarganfyddadwy gan gysylltiadau presennol rydych chi'n eu hadnabod, ychwanegwch un uchod.";
|
||||
// Identity server settings
|
||||
"identity_server_settings_title" = "Gweinydd Adnabod";
|
||||
|
@ -538,3 +538,348 @@
|
|||
"room_details_fail_to_update_room_canonical_alias" = "Methwyd diweddaru'r prif gyfeiriad";
|
||||
"room_details_fail_to_update_room_communities" = "Methwyd diweddaru'r cymunedau cysylltiedig";
|
||||
"room_details_fail_to_update_room_direct" = "Methwyd diweddaru baner uniongyrchol yr ystafell hon";
|
||||
"room_details_fail_to_enable_encryption" = "Methwyd galluogi amgryptio yn yr ystafell hon";
|
||||
"room_details_save_changes_prompt" = "Hoffech chi gadw'r newidiadau?";
|
||||
"room_details_set_main_address" = "Gosod fel Prif Gyfeiriad";
|
||||
"room_details_unset_main_address" = "Dad-osod fel Prif Gyfeiriad";
|
||||
"room_details_copy_room_id" = "Copio ID Ystafell";
|
||||
"room_details_copy_room_address" = "Copio Cyfeiriad Ystafell";
|
||||
"room_details_copy_room_url" = "Copio URL Ystafell";
|
||||
// Group Details
|
||||
"group_details_title" = "Manylion Cymuned";
|
||||
"group_details_home" = "Hafan";
|
||||
"group_details_people" = "Pobl";
|
||||
"group_details_rooms" = "Ystafelloedd";
|
||||
// Group Home
|
||||
"group_home_one_member_format" = "1 aelod";
|
||||
"group_home_multi_members_format" = "%tu aelod";
|
||||
"group_home_one_room_format" = "1 ystafell";
|
||||
"group_home_multi_rooms_format" = "%tu ystafell";
|
||||
"group_invitation_format" = "Mae %@ wedi eich gwahodd i ymuno â'r gymuned hon";
|
||||
// Group participants
|
||||
"group_participants_add_participant" = "Ychwanegu cyfranogwr";
|
||||
"group_participants_leave_prompt_title" = "Gadael grŵp";
|
||||
"group_participants_leave_prompt_msg" = "Ydych chi'n siŵr eich bod chi eisiau gadael y grŵp?";
|
||||
"group_participants_remove_prompt_title" = "Cadarnhad";
|
||||
"group_participants_remove_prompt_msg" = "Ydych chi'n siwr eich bod chi eisiau tynnu %@ o'r grŵp?";
|
||||
"group_participants_invite_prompt_title" = "Cadarnhad";
|
||||
"group_participants_invite_prompt_msg" = "Ydych chi'n siwr eich bod eisau gwahodd %@ i'r grŵp?";
|
||||
"group_participants_filter_members" = "Hidlo aelodau'r cymuned";
|
||||
"group_participants_invite_another_user" = "Chwilio / gwahodd yn ôl ID Defnyddiwr neu Enw";
|
||||
"group_participants_invite_malformed_id_title" = "Gwall gwahoddiad";
|
||||
"group_participants_invite_malformed_id" = "ID camffurfiedig. Dylai fod yn ID Matrix fel '@localpart:domain'";
|
||||
"group_participants_invited_section" = "GWAHODDWYD";
|
||||
// Group rooms
|
||||
"group_rooms_filter_rooms" = "Hidlo ystafelloedd y gymuned";
|
||||
// Read Receipts
|
||||
"read_receipts_list" = "Rhestr Derbynebau Darllen";
|
||||
"receipt_status_read" = "Wedi darllen: ";
|
||||
// Media picker
|
||||
"media_picker_title" = "Llyfrgell cyfryngau";
|
||||
"media_picker_library" = "Llyfrgell";
|
||||
"media_picker_select" = "Dewis";
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Tynnu llun";
|
||||
"image_picker_action_library" = "Dewis o'r llyfrgell";
|
||||
// Directory
|
||||
"directory_title" = "Cyfeiriadur";
|
||||
"directory_server_picker_title" = "Dewisiwch gyfeiriadur";
|
||||
"directory_server_all_rooms" = "Pob ystafell ar weinydd %@";
|
||||
"directory_server_all_native_rooms" = "Pob ystafell Matrix frodorol";
|
||||
"directory_server_type_homeserver" = "Teipiwch hafanweinydd i restru ystafelloedd cyhoeddus o";
|
||||
"directory_server_placeholder" = "matrix.org";
|
||||
// Events formatter
|
||||
"event_formatter_member_updates" = "%tu newidiadau aelodaeth";
|
||||
"event_formatter_widget_added" = "Ychwanegwyd teclyn %@ gan %@";
|
||||
"event_formatter_widget_removed" = "Tynnwyd teclyn %@ gan %@";
|
||||
"event_formatter_jitsi_widget_added" = "Ychwanegwyd cynhadledd VoIP gan %@";
|
||||
"event_formatter_jitsi_widget_removed" = "Tynnwyd cynhadledd VoIP gan %@";
|
||||
"event_formatter_rerequest_keys_part1_link" = "Ail-ofyn am allweddi amgryptio";
|
||||
"event_formatter_rerequest_keys_part2" = " o'ch dyfeisiau eraill.";
|
||||
"event_formatter_message_edited_mention" = "(adolygwyd)";
|
||||
// Others
|
||||
"or" = "neu";
|
||||
"you" = "Chi";
|
||||
"today" = "Heddiw";
|
||||
"yesterday" = "Ddoe";
|
||||
"network_offline_prompt" = "Mae'n ymddangos bod y cysylltiad Rhyngrwyd yn all-lein.";
|
||||
"homeserver_connection_lost" = "Methu cysylltu â'r hafanweinydd.";
|
||||
"public_room_section_title" = "Ystafelloedd Cyhoeddus (yn %@):";
|
||||
"bug_report_prompt" = "Daeth yr app ar draws nam pall y tro diwethaf iddo redeg. Hoffech chi gyflwyno adroddiad pall?";
|
||||
"rage_shake_prompt" = "Mae'n ymddangos eich bod yn ysgwyd y ffôn mewn rhwystredigaeth. Hoffech chi gyflwyno adroddiad nam?";
|
||||
"do_not_ask_again" = "Peidio â gofyn eto";
|
||||
"camera_access_not_granted" = "Nid oes gan %@ ganiatâd i ddefnyddio'r Camera, newidiwch y gosodiadau preifatrwydd";
|
||||
"camera_unavailable" = "Nid yw'r camera ar gael ar eich dyfais";
|
||||
"photo_library_access_not_granted" = "Nid oes gan %@ ganiatâd i gael mynediad i'r llyfrgell ffotograffau, newidiwch osodiadau preifatrwydd";
|
||||
"large_badge_value_k_format" = "%.1fK";
|
||||
"room_does_not_exist" = "Nid yw %@ yn bodoli";
|
||||
// Call
|
||||
"call_incoming_voice_prompt" = "Galwad llais sy'n dod i mewn gan %@";
|
||||
"call_incoming_video_prompt" = "Galwad fideo sy'n dod i mewn gan %@";
|
||||
"call_incoming_voice" = "Galwad sy'n dod i mewn...";
|
||||
"call_incoming_video" = "Galwad fideo sy'n dod i mewn...";
|
||||
"call_already_displayed" = "Mae galwad ar y gweill eisoes.";
|
||||
"call_jitsi_error" = "Methwyd ymuno â'r alwad cynhadledd.";
|
||||
"call_no_stun_server_error_title" = "Methodd yr alwad oherwydd gweinydd wedi'i gamosod";
|
||||
"call_no_stun_server_error_message_1" = "Gofynnwch i weinyddwr eich hafanweinydd %@ i osod gweinydd TURN er mwyn i alwadau weithio'n ddibynadwy.";
|
||||
"call_no_stun_server_error_message_2" = "Fel arall, gallwch geisio defnyddio'r gweinydd cyhoeddus yn %@, ond ni fydd hyn mor ddibynadwy, a bydd yn rhannu eich cyfeiriad IP gyda'r gweinydd hwnnw. Gallwch hefyd reoli hyn yn Gosodiadau";
|
||||
"call_no_stun_server_error_use_fallback_button" = "Rhowch gynnig ar ddefnyddio %@";
|
||||
// No VoIP support
|
||||
"no_voip_title" = "Galwad sy'n dod i mewn";
|
||||
"no_voip" = "Mae %@ yn eich ffonio ond nid yw %@ yn cefnogi galwadau eto.\nGallwch anwybyddu'r hysbysiad hwn ac ateb yr alwad o ddyfais arall neu gallwch ei wrthod.";
|
||||
// Crash report
|
||||
"google_analytics_use_prompt" = "Hoffech chi helpu i wella %@ trwy gyrru adroddiadau pall a data defnydd dienw yn awtomatig?";
|
||||
// Crypto
|
||||
"e2e_enabling_on_app_update" = "Mae Riot bellach yn cefnogi amgryptio o'r dechrau i'r diwedd ond mae angen i chi fewngofnodi eto i'w alluogi.\n\nGallwch ei wneud nawr neu'n hwyrach o'r gosodiadau.";
|
||||
"e2e_need_log_in_again" = "Mae angen i chi fewngofnodi i gynhyrchu allweddi amgryptio o'r dechrau i'r diwedd ar gyfer y ddyfais hon a chyflwyno'r allwedd gyhoeddus i'ch hafanweinydd\nDim ond unwaith fydd rhaid gwneud hyn; sori am yr anghyfleustra.";
|
||||
// Key backup wrong version
|
||||
"e2e_key_backup_wrong_version_title" = "Copi Allwedd Wrth Gefn Newydd";
|
||||
"e2e_key_backup_wrong_version" = "Mae copi allwedd wrth gefn newydd neges ddiogel wedi'i ganfod.\n\nOs nad chi oedd hyn, gosodwch gyfrinair newydd yn Gosodiadau.";
|
||||
"e2e_key_backup_wrong_version_button_settings" = "Gosodiadau";
|
||||
"e2e_key_backup_wrong_version_button_wasme" = "Fi oedd e";
|
||||
// Bug report
|
||||
"bug_report_title" = "Adroddiad Nam";
|
||||
"bug_report_description" = "Disgrifiwch y nam. Beth wnaethoch chi? Beth oeddech chi'n disgwyl iddo ddigwydd? Beth ddigwyddodd mewn gwirionedd?";
|
||||
"bug_crash_report_title" = "Adroddiad Pall";
|
||||
"bug_crash_report_description" = "Disgrifiwch yr hyn a wnaethoch cyn y pall:";
|
||||
"bug_report_logs_description" = "Er mwyn canfod problemau, anfonir logiau gan y cleient hwn gyda'r adroddiad nam hwn. Os byddai'n well gennych anfon y testun uchod yn unig, dad-gliciwch:";
|
||||
"bug_report_send_logs" = "Anfon logiau";
|
||||
"bug_report_send_screenshot" = "Anfon sgrinlun";
|
||||
"bug_report_progress_zipping" = "Casglu logiau";
|
||||
"bug_report_progress_uploading" = "Uwchlwytho adroddiad";
|
||||
"bug_report_send" = "Anfon";
|
||||
// Widget
|
||||
"widget_no_integrations_server_configured" = "Dim gweinydd integreiddiadau wedi'i osod";
|
||||
"widget_integrations_server_failed_to_connect" = "Methwyd cysylltu â'r gweinydd integreiddiadau";
|
||||
"widget_no_power_to_manage" = "Mae angen caniatâd arnoch i reoli teclynnau yn yr ystafell hon";
|
||||
"widget_creation_failure" = "Methwyd creu teclyn";
|
||||
"widget_sticker_picker_no_stickerpacks_alert" = "Ar hyn o bryd nid oes gennych unrhyw becyn sticeri wedi'u galluogi.";
|
||||
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Ychwanegu rhai rwan?";
|
||||
// Widget Integration Manager
|
||||
"widget_integration_need_to_be_able_to_invite" = "Mae angen i chi allu gwahodd defnyddwyr i wneud hynny.";
|
||||
"widget_integration_unable_to_create" = "Methu creu teclyn.";
|
||||
"widget_integration_failed_to_send_request" = "Methwyd i anfon cais.";
|
||||
"widget_integration_room_not_recognised" = "Ni gydnabyddir yr ystafell hon.";
|
||||
"widget_integration_positive_power_level" = "Rhaid i lefel pŵer fod yn gyfanrif positif.";
|
||||
"widget_integration_must_be_in_room" = "Nid ydych yn yr ystafell hon.";
|
||||
"widget_integration_no_permission_in_room" = "Nid oes gennych ganiatâd i wneud hynny yn yr ystafell hon.";
|
||||
"widget_integration_missing_room_id" = "room_id ar goll yn y cais.";
|
||||
"widget_integration_missing_user_id" = "user_id ar goll yn y cais.";
|
||||
"widget_integration_room_not_visible" = "Nid yw ystafell %@ yn weladwy.";
|
||||
// Widget Picker
|
||||
"widget_picker_title" = "Integreiddiadau";
|
||||
// Share extension
|
||||
"share_extension_auth_prompt" = "Mewngofnodwch yn y prif app i rannu cynnwys";
|
||||
"share_extension_failed_to_encrypt" = "Methwyd anfon. Gwiriwch y gosodiadau amgryptio ar gyfer yr ystafell hon yn y prif app";
|
||||
// Room key request dialog
|
||||
"e2e_room_key_request_title" = "Cais allwedd amgryptio";
|
||||
"e2e_room_key_request_message_new_device" = "Fe wnaethoch ychwanegu dyfais newydd '%@', sy'n gofyn am allweddi amgryptio.";
|
||||
"e2e_room_key_request_message" = "Mae'ch dyfais anwiriedig '%@' yn gofyn am allweddi amgryptio.";
|
||||
"e2e_room_key_request_start_verification" = "Dechrau gwirio...";
|
||||
"e2e_room_key_request_share_without_verifying" = "Rhannwch heb wirio";
|
||||
"e2e_room_key_request_ignore_request" = "Anwybyddu cais";
|
||||
// GDPR
|
||||
"gdpr_consent_not_given_alert_message" = "Er mwyn parhau i ddefnyddio'r hafanweinydd %@ rhaid i chi adolygu a chytuno i'r telerau ac amodau.";
|
||||
"gdpr_consent_not_given_alert_review_now_action" = "Adolygu rwan";
|
||||
// Service terms
|
||||
"service_terms_modal_title" = "Telerau Gwasanaeth";
|
||||
"service_terms_modal_message" = "I barhau maen' rhaid i chi dderbyn telerau y gwasanaeth hwn (%@).";
|
||||
"service_terms_modal_accept_button" = "Derbyn";
|
||||
"service_terms_modal_decline_button" = "Gwrthod";
|
||||
"service_terms_modal_description_for_identity_server_1" = "Dod o hyd i eraill dros y ffôn neu e-bost";
|
||||
"service_terms_modal_description_for_identity_server_2" = "Byddwch i'w gweld dros ffôn neu e-bost";
|
||||
"service_terms_modal_description_for_integration_manager" = "Defnyddiwch Botiau, pontydd, teclynnau a phecynnau sticeri";
|
||||
// Service terms - Variant for identity server when displayed out of a context
|
||||
"service_terms_modal_title_identity_server" = "Darganfod Cysylltiadau";
|
||||
"service_terms_modal_message_identity_server" = "Derbyn telerau'r gweinydd adnabod (%@) i ddarganfod cysylltiadau.";
|
||||
"deactivate_account_title" = "Dad-actifadu Cyfrif";
|
||||
"deactivate_account_informations_part1" = "Bydd hyn yn golygu na ellir defnyddio'ch cyfrif yn barhaol. Ni fyddwch yn gallu mewngofnodi, ac ni fydd unrhyw un yn gallu ailgofrestru'r un ID defnyddiwr. Bydd hyn yn achosi i'ch cyfrif adael yr holl ystafelloedd y mae'n cymryd rhan ynddynt, a bydd yn tynnu manylion eich cyfrif o'ch gweinydd adnabod. ";
|
||||
"deactivate_account_informations_part2_emphasize" = "Ni ellir gwrthdroi'r weithred hon.";
|
||||
"deactivate_account_informations_part3" = "\n\nYn dad-actifadu eich cyfrif ";
|
||||
"deactivate_account_informations_part4_emphasize" = "nid yw yn ddiofyn yn achosi inni anghofio negeseuon yr ydych wedi'u hanfon. ";
|
||||
"deactivate_account_informations_part5" = "Os hoffech i ni anghofio'ch negeseuon, ticiwch y blwch isod\n\nMae gwelededd neges yn Matrix yn debyg i e-bost. Mae anghofio'ch negeseuon yn golygu na fydd negeseuon rydych chi wedi'u hanfon yn cael eu rhannu ag unrhyw ddefnyddwyr newydd neu anghofrestredig, ond bydd defnyddwyr cofrestredig sydd eisoes â mynediad at y negeseuon hyn yn dal i gael mynediad i'w copi.";
|
||||
"deactivate_account_forget_messages_information_part1" = "Anghofiwch yr holl negeseuon yr wyf wedi'u hanfon pan fydd fy nghyfrif yn cael ei ddad-actifadu (";
|
||||
"deactivate_account_forget_messages_information_part2_emphasize" = "Rhybudd";
|
||||
"deactivate_account_forget_messages_information_part3" = ": bydd hyn yn achosi i ddefnyddwyr y dyfodol weld golwg anghyflawn o sgyrsiau)";
|
||||
"deactivate_account_validate_action" = "Dad-actifadu cyfrif";
|
||||
"deactivate_account_password_alert_title" = "Dad-actifadu Cyfrif";
|
||||
"deactivate_account_password_alert_message" = "I barhau, rhowch eich cyfrinair os gwelwch yn dda";
|
||||
// Re-request confirmation dialog
|
||||
"rerequest_keys_alert_title" = "Anfonwyd y Cais";
|
||||
"rerequest_keys_alert_message" = "Lansiwch Riot ar ddyfais arall a all ddadgryptio'r neges fel y gall anfon yr allweddi i'r ddyfais hon.";
|
||||
"key_backup_setup_title" = "Allweddi Wrth Gefn";
|
||||
"key_backup_setup_skip_alert_title" = "Ydych chi'n siwr?";
|
||||
"key_backup_setup_skip_alert_message" = "Efallai y byddwch chi'n colli negeseuon diogel os byddwch chi'n allgofnodi neu'n colli'ch dyfais.";
|
||||
"key_backup_setup_skip_alert_skip_action" = "Sgipio";
|
||||
"key_backup_setup_intro_title" = "Peidiwch byth â cholli negeseuon wedi'u hamgryptio";
|
||||
"key_backup_setup_intro_info" = "Diogelir negeseuon mewn ystafelloedd amgryptiedig gydag amgryptio o'r dechrau i'r diwedd. Dim ond chi a'r derbynnydd / derbynwyr sydd â'r allweddi i ddarllen y negeseuon hyn.\n\nGwnewch copi wrth gefn o'ch allweddi yn ddiogel er mwyn osgoi eu colli.";
|
||||
"key_backup_setup_intro_setup_action_without_existing_backup" = "Dechrau defnyddio Allweddi Wrth Gefn";
|
||||
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Cysylltwch y ddyfais hon i Allweddi Wrth Gefn";
|
||||
"key_backup_setup_intro_manual_export_info" = "(Uwch)";
|
||||
"key_backup_setup_intro_manual_export_action" = "Allfudo allweddi â llaw";
|
||||
"key_backup_setup_passphrase_title" = "Diogelwch eich copi wrth gefn gyda chyfrinair";
|
||||
"key_backup_setup_passphrase_info" = "Byddwn yn storio copi wedi'i amgryptio o'ch allweddi ar ein gweinydd. Amddiffynwch eich copi wrth gefn gyda chyfrinair i'w gadw'n ddiogel.\n\nEr mwyn sicrhau'r diogelwch mwyaf, dylai hyn fod yn wahanol i gyfrinair eich cyfrif.";
|
||||
"key_backup_setup_passphrase_passphrase_title" = "Cyfrinair";
|
||||
"key_backup_setup_passphrase_passphrase_placeholder" = "Rhowch cyfrinair";
|
||||
"key_backup_setup_passphrase_passphrase_valid" = "Gwych!";
|
||||
"key_backup_setup_passphrase_passphrase_invalid" = "Ceisiwch ychwanegu gair";
|
||||
"key_backup_setup_passphrase_confirm_passphrase_title" = "Cadarnhau";
|
||||
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Cadarnhau cyfrinair";
|
||||
"key_backup_setup_passphrase_confirm_passphrase_valid" = "Gwych!";
|
||||
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Nid yw'r cyfrinair yn cyfateb";
|
||||
"key_backup_setup_passphrase_set_passphrase_action" = "Cadw Cyfrinair";
|
||||
"key_backup_setup_passphrase_setup_recovery_key_info" = "Neu, diogelwch eich copi wrth gefn gydag Allwedd Adfer, gan ei arbed yn rhywle diogel.";
|
||||
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Uwch) Gosod gydag Allwedd Adfer";
|
||||
"key_backup_setup_success_title" = "Llwyddiant!";
|
||||
// Success from passphrase
|
||||
"key_backup_setup_success_from_passphrase_info" = "Mae'ch allweddi yn cael eu cadw wrth gefn.\n\nRhwyd ddiogelwch yw eich allwedd adfer - gallwch ei defnyddio i adfer mynediad i'ch negeseuon amgryptiedig os byddwch chi'n anghofio'ch cyfrinair.\n\nCadwch eich allwedd adfer yn rhywle diogel iawn, fel rheolwr cyfrinair (neu dan glo).";
|
||||
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Cadw Allwedd Adfer";
|
||||
"key_backup_setup_success_from_passphrase_done_action" = "Wedi Gorffen";
|
||||
// Success from recovery key
|
||||
"key_backup_setup_success_from_recovery_key_info" = "Mae'ch allweddi yn cael eu cadw wrth gefn.\n\nGwnewch gopi o'r allwedd adfer hon a'i chadw'n ddiogel.";
|
||||
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Allwedd Adfer";
|
||||
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Gwneud Copi";
|
||||
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Dwi wedi gwneud copi";
|
||||
"key_backup_recover_title" = "Negeseuon Diogel";
|
||||
"key_backup_recover_invalid_passphrase_title" = "Cyfrinair Adfer Anghywir";
|
||||
"key_backup_recover_invalid_passphrase" = "Ni ellid dadgryptio copi wrth gefn gyda'r cyfrinair hwn: gwiriwch eich bod wedi nodi'r cyfrinair adfer cywir.";
|
||||
"key_backup_recover_invalid_recovery_key_title" = "Camgymhariad Allwedd Adfer";
|
||||
"key_backup_recover_invalid_recovery_key" = "Ni ellid dadgryptio copi wrth gefn gyda'r allwedd hon: gwiriwch eich bod wedi nodi'r allwedd adfer gywir.";
|
||||
"key_backup_recover_from_passphrase_info" = "Defnyddiwch eich cyfrinair adfer i ddatgloi eich hanes neges ddiogel";
|
||||
"key_backup_recover_from_passphrase_passphrase_title" = "Cyfrinair";
|
||||
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Rhowch cyfrinair";
|
||||
"key_backup_recover_from_passphrase_recover_action" = "Datgloi Hanes";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Ddim yn gwybod eich cyfrinair adfer? Gallwch chi ";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "ddefnyddio eich allwedd adfer";
|
||||
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
|
||||
"key_backup_recover_from_recovery_key_info" = "Defnyddiwch eich allwedd adfer i ddatgloi eich hanes neges ddiogel";
|
||||
"key_backup_recover_from_recovery_key_recovery_key_title" = "Allwedd Adfer";
|
||||
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Rhowch Allwedd Adfer";
|
||||
"key_backup_recover_from_recovery_key_recover_action" = "Datgloi Hanes";
|
||||
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Wedi colli'ch allwedd adfer? Gallwch chi sefydlu un newydd yn gosodiadau.";
|
||||
"key_backup_recover_success_info" = "Copi wrth gefn wedi'i Adfer!";
|
||||
"key_backup_recover_done_action" = "Wedi Gorffen";
|
||||
"key_backup_setup_banner_title" = "Peidiwch byth â cholli negeseuon wedi'u hamgryptio";
|
||||
"key_backup_setup_banner_subtitle" = "Dechrau defnyddio Allweddi Wrth Gefn";
|
||||
"key_backup_recover_banner_title" = "Peidiwch byth â cholli negeseuon wedi'u hamgryptio";
|
||||
"key_backup_recover_connent_banner_subtitle" = "Cysylltwch y ddyfais hon i Allweddi Wrth Gefn";
|
||||
"sign_out_existing_key_backup_alert_title" = "Ydych chi'n siŵr eich bod chi am allgofnodi?";
|
||||
"sign_out_existing_key_backup_alert_sign_out_action" = "Allgofnodi";
|
||||
"sign_out_non_existing_key_backup_alert_title" = "Byddwch yn colli mynediad i'ch negeseuon amgryptiedig os byddwch chi'n allgofnodi rwan";
|
||||
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Dechrau defnyddio Allweddi Wrth Gefn";
|
||||
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Dydw i ddim eisiau i fy negeseuon amgryptiedig";
|
||||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Byddwch yn colli ei'ch negeseuon amgryptiedig";
|
||||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Byddwch yn colli mynediad i'ch negeseuon amgryptiedig oni bai eich bod yn gwneud copi wrth gefn o'ch allweddi cyn allgofnodi.";
|
||||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Allgofnodi";
|
||||
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Creu Copi Wrth Gefn";
|
||||
"sign_out_key_backup_in_progress_alert_title" = "Creu copi allwedd wrth gefn ar y gweill. Os byddwch chi'n allgofnodi nawr byddwch chi'n colli mynediad i'ch negeseuon wedi'u hamgryptio.";
|
||||
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Dydw i ddim eisiau i fy negeseuon amgryptiedig";
|
||||
"sign_out_key_backup_in_progress_alert_cancel_action" = "Arosaf";
|
||||
// MARK: - Device Verification
|
||||
"device_verification_title" = "Gwirio dyfais";
|
||||
"device_verification_security_advice" = "Er mwyn sicrhau'r diogelwch mwyaf, rydym yn argymell eich bod yn gwneud hyn yn bersonol neu'n defnyddio dull cyfathrebu dibynadwy arall";
|
||||
"device_verification_cancelled" = "Canslodd y parti arall y gwiriad.";
|
||||
"device_verification_cancelled_by_me" = "Mae'r gwiriad wedi'i ganslo. Rheswm: %@";
|
||||
"device_verification_error_cannot_load_device" = "Methu llwytho gwybodaeth am ddyfeisiau.";
|
||||
// Mark: Incoming
|
||||
"device_verification_incoming_title" = "Cais Gwirio sy'n Dod i Mewn";
|
||||
"device_verification_incoming_description_1" = "Gwiriwch y ddyfais hon i'w nodi fel un y gellir ymddiried ynddo. Mae dyfeisiau ymddiriedol partneriaid yn rhoi tawelwch meddwl ychwanegol i chi wrth ddefnyddio negeseuon wedi'u hamgryptio o'r dechrau i'r diwedd.";
|
||||
"device_verification_incoming_description_2" = "Bydd gwirio'r ddyfais hon yn ei nodi fel un y gellir ymddiried ynddo, a hefyd yn marcio'ch dyfais fel un y gellir ymddiried ynddo i'r partner.";
|
||||
// MARK: Start
|
||||
"device_verification_start_title" = "Gwirio trwy gymharu testun byr";
|
||||
"device_verification_start_wait_partner" = "Aros i'r partner dderbyn ...";
|
||||
"device_verification_start_use_legacy" = "Dim byd yn ymddangos? Nid yw pob cleient yn cefnogi gwirio rhyngweithiol eto. Defnyddiwch yr hen fodd o wirio.";
|
||||
"device_verification_start_verify_button" = "Dechrau Gwirio";
|
||||
"device_verification_start_use_legacy_action" = "Defnyddio'r Hen Fodd o Wirio";
|
||||
// MARK: Verify
|
||||
"device_verification_verify_title_emoji" = "Gwiriwch y ddyfais hon trwy gadarnhau'r emoji canlynol sy'n ymddangos ar sgrin y partner";
|
||||
"device_verification_verify_title_number" = "Gwiriwch y ddyfais hon trwy gadarnhau'r rhifau canlynol sy'n ymddangos ar sgrin y partner";
|
||||
"device_verification_verify_wait_partner" = "Aros i'r partner gadarnhau...";
|
||||
// MARK: Verified
|
||||
"device_verification_verified_title" = "Wedi Gwirio!";
|
||||
"device_verification_verified_description_1" = "Rydych chi wedi gwirio'r ddyfais hon yn llwyddiannus.";
|
||||
"device_verification_verified_description_2" = "Mae negeseuon diogel gyda'r defnyddiwr hwn wedi'u hamgryptio o'r dechrau i'r diwedd ac ni all unrhyw drydydd parti eu darllen.";
|
||||
"device_verification_verified_got_it_button" = "Iawn";
|
||||
// MARK: Emoji
|
||||
"device_verification_emoji_dog" = "Ci";
|
||||
"device_verification_emoji_cat" = "Cath";
|
||||
"device_verification_emoji_lion" = "Llew";
|
||||
"device_verification_emoji_horse" = "Ceffyl";
|
||||
"device_verification_emoji_unicorn" = "Ungorn";
|
||||
"device_verification_emoji_pig" = "Mochyn";
|
||||
"device_verification_emoji_elephant" = "Eliffant";
|
||||
"device_verification_emoji_rabbit" = "Cwningen";
|
||||
"device_verification_emoji_panda" = "Panda";
|
||||
"device_verification_emoji_rooster" = "Ceiliog";
|
||||
"device_verification_emoji_penguin" = "Pengwin";
|
||||
"device_verification_emoji_turtle" = "Crwban";
|
||||
"device_verification_emoji_fish" = "Pysgodyn";
|
||||
"device_verification_emoji_octopus" = "Octopws";
|
||||
"device_verification_emoji_butterfly" = "Pilipala";
|
||||
"device_verification_emoji_flower" = "Blodyn";
|
||||
"device_verification_emoji_tree" = "Coeden";
|
||||
"device_verification_emoji_cactus" = "Cactws";
|
||||
"device_verification_emoji_mushroom" = "Madarchen";
|
||||
"device_verification_emoji_globe" = "Glôb";
|
||||
"device_verification_emoji_moon" = "Lleuad";
|
||||
"device_verification_emoji_cloud" = "Cwmwl";
|
||||
"device_verification_emoji_fire" = "Tân";
|
||||
"device_verification_emoji_banana" = "Banana";
|
||||
"device_verification_emoji_apple" = "Afal";
|
||||
"device_verification_emoji_strawberry" = "Mefusen";
|
||||
"device_verification_emoji_corn" = "Ŷd";
|
||||
"device_verification_emoji_pizza" = "Pizza";
|
||||
"device_verification_emoji_cake" = "Cacen";
|
||||
"device_verification_emoji_heart" = "Calon";
|
||||
"device_verification_emoji_smiley" = "Gwenoglun";
|
||||
"device_verification_emoji_robot" = "Robot";
|
||||
"device_verification_emoji_hat" = "Het";
|
||||
"device_verification_emoji_glasses" = "Sbectol";
|
||||
"device_verification_emoji_spanner" = "Sbaner";
|
||||
"device_verification_emoji_santa" = "Siôn Corn";
|
||||
"device_verification_emoji_thumbs up" = "Codi bawd";
|
||||
"device_verification_emoji_umbrella" = "Ymbarél";
|
||||
"device_verification_emoji_hourglass" = "Awrwydr";
|
||||
"device_verification_emoji_clock" = "Cloc";
|
||||
"device_verification_emoji_gift" = "Rhodd";
|
||||
"device_verification_emoji_light bulb" = "Bwlb Golau";
|
||||
"device_verification_emoji_book" = "Llyfr";
|
||||
"device_verification_emoji_pencil" = "Pensil";
|
||||
"device_verification_emoji_paperclip" = "Clip Papur";
|
||||
"device_verification_emoji_scissors" = "Siswrn";
|
||||
"device_verification_emoji_lock" = "Clo";
|
||||
"device_verification_emoji_key" = "Allwedd";
|
||||
"device_verification_emoji_hammer" = "Mwrthwl";
|
||||
"device_verification_emoji_telephone" = "Ffôn";
|
||||
"device_verification_emoji_flag" = "Baner";
|
||||
"device_verification_emoji_train" = "Trên";
|
||||
"device_verification_emoji_bicycle" = "Beic";
|
||||
"device_verification_emoji_aeroplane" = "Awyren";
|
||||
"device_verification_emoji_rocket" = "Roced";
|
||||
"device_verification_emoji_trophy" = "Tlws";
|
||||
"device_verification_emoji_ball" = "Pêl";
|
||||
"device_verification_emoji_guitar" = "Gitâr";
|
||||
"device_verification_emoji_trumpet" = "Trwmped";
|
||||
"device_verification_emoji_bell" = "Cloch";
|
||||
"device_verification_emoji_anchor" = "Angor";
|
||||
"device_verification_emoji_headphones" = "Clustffonau";
|
||||
"device_verification_emoji_folder" = "Plygell";
|
||||
"device_verification_emoji_pin" = "Pin";
|
||||
// MARK: File upload
|
||||
"file_upload_error_title" = "Uwchlwythiad Ffeil";
|
||||
"file_upload_error_unsupported_file_type_message" = "Ni gefnogir y yma math o ffeil.";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Ymatebion";
|
||||
"emoji_picker_people_category" = "Gwenogluniau & Pobl";
|
||||
"emoji_picker_nature_category" = "Anifeiliaid & Natur";
|
||||
"emoji_picker_foods_category" = "Bwyd & Diod";
|
||||
"emoji_picker_activity_category" = "Gweithgareddau";
|
||||
"emoji_picker_places_category" = "Teithio & Llefydd";
|
||||
"emoji_picker_objects_category" = "Gwrthrychau";
|
||||
"emoji_picker_symbols_category" = "Symbolau";
|
||||
"emoji_picker_flags_category" = "Baneri";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Ymatebion";
|
||||
// Generic errors
|
||||
"error_invite_3pid_with_no_identity_server" = "Ychwanegwch weinydd adnabod yn eich gosodiadau i wahodd trwy e-bost.";
|
||||
"error_not_supported_on_mobile" = "Ni allwch wneud hyn o %@ ffôn symudol.";
|
||||
|
|
|
@ -897,3 +897,10 @@
|
|||
"settings_add_3pid_invalid_password_message" = "Ungültiges Passwort";
|
||||
"identity_server_settings_disconnect_info" = "Wenn Sie die Verbindung zu Ihrem Identitätsserver trennen, werden Sie von anderen Benutzern nicht erkannt und können andere per E-Mail oder Telefon einladen.";
|
||||
"error_not_supported_on_mobile" = "Dies ist in %@ mobile nicht möglich.";
|
||||
"settings_integrations" = "INTEGRATIONEN";
|
||||
"settings_integrations_allow_button" = "Integrationen verwalten";
|
||||
"widget_menu_refresh" = "Aktualisierung";
|
||||
"widget_menu_open_outside" = "Im Browser öffnen";
|
||||
"widget_menu_revoke_permission" = "Zugriff für mich widerrufen";
|
||||
"widget_menu_remove" = "Für alle entfernen";
|
||||
"widget_integration_manager_disabled" = "Sie müssen den Integration Manager in den Einstellungen aktivieren";
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
"sending" = "Sending";
|
||||
"close" = "Close";
|
||||
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "checkbox";
|
||||
|
||||
// Authentication
|
||||
"auth_login" = "Log in";
|
||||
"auth_register" = "Register";
|
||||
|
@ -378,6 +381,7 @@
|
|||
"settings_calls_settings" = "CALLS";
|
||||
"settings_discovery_settings" = "DISCOVERY";
|
||||
"settings_identity_server_settings" = "IDENTITY SERVER";
|
||||
"settings_integrations" = "INTEGRATIONS";
|
||||
"settings_user_interface" = "USER INTERFACE";
|
||||
"settings_ignored_users" = "IGNORED USERS";
|
||||
"settings_contacts" = "LOCAL CONTACTS";
|
||||
|
@ -431,6 +435,9 @@
|
|||
"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_integrations_allow_button" = "Manage integrations";
|
||||
"settings_integrations_allow_description" = "Use an Integration Manager (%@) to manage bots, bridges, widgets and sticker packs.\n\nIntegration Managers receive configuration data, and can modify widgets, send room invites and set power levels on your behalf.";
|
||||
|
||||
"settings_ui_language" = "Language";
|
||||
"settings_ui_theme" = "Theme";
|
||||
"settings_ui_theme_auto" = "Auto";
|
||||
|
@ -451,6 +458,8 @@
|
|||
"settings_labs_room_members_lazy_loading_error_message" = "Your homeserver does not support lazy loading of room members yet. Try later.";
|
||||
"settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi";
|
||||
"settings_labs_message_reaction" = "React to messages with emoji";
|
||||
"settings_labs_dm_key_verification" = "Key verification by direct message";
|
||||
"settings_labs_cross_signing" = "Cross-Signing";
|
||||
|
||||
"settings_version" = "Version %@";
|
||||
"settings_olm_version" = "Olm Version %@";
|
||||
|
@ -755,6 +764,10 @@
|
|||
"widget_creation_failure" = "Widget creation has failed";
|
||||
"widget_sticker_picker_no_stickerpacks_alert" = "You don't currently have any stickerpacks enabled.";
|
||||
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Add some now?";
|
||||
"widget_menu_refresh" = "Refresh";
|
||||
"widget_menu_open_outside" = "Open in browser";
|
||||
"widget_menu_revoke_permission" = "Revoke access for me";
|
||||
"widget_menu_remove" = "Remove for everyone";
|
||||
|
||||
// Widget Integration Manager
|
||||
"widget_integration_need_to_be_able_to_invite" = "You need to be able to invite users to do that.";
|
||||
|
@ -767,9 +780,26 @@
|
|||
"widget_integration_missing_room_id" = "Missing room_id in request.";
|
||||
"widget_integration_missing_user_id" = "Missing user_id in request.";
|
||||
"widget_integration_room_not_visible" = "Room %@ is not visible.";
|
||||
"widget_integration_manager_disabled" = "You need to enable Integration Manager in settings";
|
||||
|
||||
// Widget Picker
|
||||
"widget_picker_title" = "Integrations";
|
||||
"widget_picker_manage_integrations" = "Manage integrations...";
|
||||
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Load Widget";
|
||||
"room_widget_permission_creator_info_title" = "This widget was added by:";
|
||||
|
||||
"room_widget_permission_webview_information_title" = "Using it may set cookies and share data with %@:\n";
|
||||
|
||||
"room_widget_permission_information_title" = "Using it may share data with %@:\n";
|
||||
|
||||
"room_widget_permission_display_name_permission" = "Your display name";
|
||||
"room_widget_permission_avatar_url_permission" = "Your avatar URL";
|
||||
"room_widget_permission_user_id_permission" = "Your user ID";
|
||||
"room_widget_permission_theme_permission" = "Your theme";
|
||||
"room_widget_permission_widget_id_permission" = "Widget ID";
|
||||
"room_widget_permission_room_id_permission" = "Room ID";
|
||||
|
||||
// Share extension
|
||||
"share_extension_auth_prompt" = "Login in the main app to share content";
|
||||
|
@ -801,6 +831,7 @@
|
|||
"service_terms_modal_title_identity_server" = "Contact discovery";
|
||||
"service_terms_modal_message_identity_server" = "Accept the terms of the identity server (%@) to discover contacts.";
|
||||
|
||||
"service_terms_modal_policy_checkbox_accessibility_hint" = "Check to accept %@";
|
||||
|
||||
// Deactivate account
|
||||
|
||||
|
|
|
@ -904,3 +904,23 @@
|
|||
"settings_add_3pid_password_message" = "Jarraitzeko sartu zure pasahitza";
|
||||
"settings_add_3pid_invalid_password_message" = "Pasahitz baliogabea";
|
||||
"error_not_supported_on_mobile" = "Ezin duzu hau %@ mugikorretik egin.";
|
||||
"settings_integrations" = "INTEGRAZIOAK";
|
||||
"settings_integrations_allow_button" = "Kudeatu integrazioak";
|
||||
"settings_integrations_allow_description" = "Erabili integrazio kudeatzaileren bat botak, zubiak, trepetak eta eranskailu multzoak kudeatzeko.\n\nIntegrazio kudeatzaileek konfigurazio datuak jasotzen dituzte, eta trepetak aldatu ditzakete, gelarako gonbidapenak bidali, eta botere mailak zure izenean ezarri.";
|
||||
"widget_menu_refresh" = "Freskatu";
|
||||
"widget_menu_open_outside" = "Ireki nabigatzailean";
|
||||
"widget_menu_revoke_permission" = "Indargabetu sarbidea niretzat";
|
||||
"widget_menu_remove" = "Kendu denentzat";
|
||||
"widget_integration_manager_disabled" = "Integrazio kudeatzaileak gaitu behar dituzu ezarpenetan";
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Kargatu trepeta";
|
||||
"room_widget_permission_creator_info_title" = "Trepeta hau honek gehitu du:";
|
||||
"room_widget_permission_webview_information_title" = "Hau erabiltzean cookieak ezarri litezke eta %@ zerbitzariarekin datuak partekatu:\n";
|
||||
"room_widget_permission_information_title" = "Hau erabiltzean %@ zerbitzariarekin datuak partekatu litezke:\n";
|
||||
"room_widget_permission_display_name_permission" = "Zure pantaila-izena";
|
||||
"room_widget_permission_avatar_url_permission" = "Zure abatarraren URL-a";
|
||||
"room_widget_permission_user_id_permission" = "Zure erabiltzaile ID-a";
|
||||
"room_widget_permission_theme_permission" = "Zure gaia";
|
||||
"room_widget_permission_widget_id_permission" = "Trepetaren ID-a";
|
||||
"room_widget_permission_room_id_permission" = "Gelaren ID-a";
|
||||
"widget_picker_manage_integrations" = "Integrazioak kudeatu...";
|
||||
|
|
|
@ -916,3 +916,31 @@
|
|||
"settings_add_3pid_password_message" = "Pour continuer, saisissez votre mot de passe";
|
||||
"settings_add_3pid_invalid_password_message" = "Mot de passe non valide";
|
||||
"error_not_supported_on_mobile" = "Vous ne pouvez pas faire cela depuis %@ mobile.";
|
||||
"widget_menu_refresh" = "Actualiser";
|
||||
"widget_menu_open_outside" = "Ouvrir dans le navigateur";
|
||||
"widget_menu_revoke_permission" = "Révoquer l’accès pour moi";
|
||||
"widget_menu_remove" = "Supprimer pour tout le monde";
|
||||
"settings_integrations" = "INTÉGRATIONS";
|
||||
"settings_integrations_allow_button" = "Gérer les intégrations";
|
||||
"settings_integrations_allow_description" = "Utilisez un gestionnaire d’intégrations (%@) pour gérer les bots, les passerelles, les widgets et les packs de stickers.\n\nLes gestionnaires d’intégration reçoivent des données de configuration et peuvent modifier les widgets, envoyer des invitations de salon et définir des rangs à votre place.";
|
||||
"widget_integration_manager_disabled" = "Vous devez activer le gestionnaire d’intégrations dans les paramètres";
|
||||
"widget_room_permission_title" = "Charger le widget";
|
||||
"widget_room_permission_creator_info_title" = "Ce widget a été ajouté par :";
|
||||
"widget_room_permission_information" = "Son utilisation peut utiliser des cookies et partager des données avec %@ :\n\n• Votre nom affiché\n• L’URL de votre avatar\n• Votre identifiant d’utilisateur\n• Votre thème\n• L’identifiant du salon\n• L’identifiant du widget";
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Charger un widget";
|
||||
"room_widget_permission_creator_info_title" = "Ce widget a été ajouté par :";
|
||||
"room_widget_permission_webview_information_title" = "Son utilisation peut entraîner l’utilisation de cookies et le partage de données avec %@ :\n";
|
||||
"room_widget_permission_information_title" = "Son utilisation peut entraîner le partage de données avec %@ :\n";
|
||||
"room_widget_permission_display_name_permission" = "Votre nom affiché";
|
||||
"room_widget_permission_avatar_url_permission" = "L’URL de votre avatar";
|
||||
"room_widget_permission_user_id_permission" = "Votre identifiant d’utilisateur";
|
||||
"room_widget_permission_theme_permission" = "Votre thème";
|
||||
"room_widget_permission_widget_id_permission" = "L’identifiant du widget";
|
||||
"room_widget_permission_room_id_permission" = "L’identifiant du salon";
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "case à cocher";
|
||||
"widget_picker_manage_integrations" = "Gérer les intégrations…";
|
||||
"service_terms_modal_policy_checkbox_accessibility_hint" = "Cochez pour accepter %@";
|
||||
"settings_labs_dm_key_verification" = "Vérification de clé par message direct";
|
||||
"settings_labs_cross_signing" = "Signature croisée";
|
||||
|
|
|
@ -921,3 +921,28 @@
|
|||
"settings_add_3pid_password_message" = "A folytatáshoz add meg a jelszavadat";
|
||||
"settings_add_3pid_invalid_password_message" = "Érvénytelen jelszó";
|
||||
"error_not_supported_on_mobile" = "%@ mobilról ezt nem teheted meg.";
|
||||
"widget_menu_refresh" = "Frissítés";
|
||||
"widget_menu_open_outside" = "Megnyitás böngészőben";
|
||||
"widget_menu_revoke_permission" = "Hozzáférés megvonása magamtól";
|
||||
"widget_menu_remove" = "Visszavonás mindenkitől";
|
||||
"settings_integrations" = "INTEGRÁCIÓK";
|
||||
"settings_integrations_allow_button" = "Integrációk kezelése";
|
||||
"settings_integrations_allow_description" = "Botok, hidak, kisalkalmazások és matrica csomagok kezeléséhez használj Integrációs Menedzsert (%@).\n\nIntegrációs Menedzser megkapja a konfigurációt, módosíthat kisalkalmazásokat, szobához meghívót küldhet és a hozzáférési szintet beállíthatja helyetted.";
|
||||
"widget_integration_manager_disabled" = "Az integrációs menedzsert engedélyezned kell a beállításokban";
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Kisalkalmazás betöltése";
|
||||
"room_widget_permission_creator_info_title" = "Ezt a kisalkalmazást hozzáadta:";
|
||||
"room_widget_permission_webview_information_title" = "A használatához lehet, hogy sütiket kell használni és adat lesz megosztva ezzel: %@:\n";
|
||||
"room_widget_permission_information_title" = "A használatához lehet, hogy adat lesz megosztva ezzel: %@:\n";
|
||||
"room_widget_permission_display_name_permission" = "Megjelenítési neved";
|
||||
"room_widget_permission_avatar_url_permission" = "Profilképed URL-je";
|
||||
"room_widget_permission_user_id_permission" = "Felhasználói azonosítód";
|
||||
"room_widget_permission_theme_permission" = "Témád";
|
||||
"room_widget_permission_widget_id_permission" = "Kisalkalmazás azon.";
|
||||
"room_widget_permission_room_id_permission" = "Szoba azonosító";
|
||||
"widget_picker_manage_integrations" = "Integrációk kezelése…";
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "jelölőnégyzet";
|
||||
"service_terms_modal_policy_checkbox_accessibility_hint" = "Az engedélyezéshez jelöld be: %@";
|
||||
"settings_labs_dm_key_verification" = "Kulcs ellenőrzés közvetlen üzenetben";
|
||||
"settings_labs_cross_signing" = "Kereszt-aláírás";
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"title_groups" = "Comunità";
|
||||
"warning" = "Attenzione";
|
||||
"next" = "Prossimo";
|
||||
"leave" = "Lascia";
|
||||
"leave" = "Esci";
|
||||
"remove" = "Rimuovi";
|
||||
"invite" = "Invita";
|
||||
"cancel" = "Annulla";
|
||||
|
@ -61,7 +61,7 @@
|
|||
// String for App Store
|
||||
"store_short_description" = "Conversazioni sicure e decentralizzate";
|
||||
// Actions
|
||||
"view" = "Vedi";
|
||||
"view" = "Visualizza";
|
||||
"back" = "Indietro";
|
||||
"continue" = "Continua";
|
||||
"create" = "Crea";
|
||||
|
@ -391,7 +391,7 @@
|
|||
"settings_key_backup_delete_confirmation_prompt_title" = "Elimina backup";
|
||||
"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_title" = "Dettagli canale";
|
||||
"room_details_people" = "Membri";
|
||||
"room_details_files" = "File";
|
||||
"room_details_settings" = "Impostazioni";
|
||||
|
@ -891,3 +891,28 @@
|
|||
"settings_add_3pid_password_message" = "Per continuare, inserisci la tua password";
|
||||
"settings_add_3pid_invalid_password_message" = "Password non valida";
|
||||
"error_not_supported_on_mobile" = "Non puoi farlo da %@ mobile.";
|
||||
"widget_menu_refresh" = "Ricarica";
|
||||
"widget_menu_open_outside" = "Apri nel browser";
|
||||
"widget_menu_revoke_permission" = "Revoca l'accesso a me";
|
||||
"widget_menu_remove" = "Rimuovi per tutti";
|
||||
"settings_integrations" = "INTEGRAZIONI";
|
||||
"settings_integrations_allow_button" = "Gestisci le integrazioni";
|
||||
"settings_integrations_allow_description" = "Usa un gestore di integrazioni (%@) per gestire bot, bridge, widget e pacchetti di adesivi.\n\nI gestori di integrazione ricevono dati di configurazione e possono modificare widget, inviare inviti alla stanza, assegnare permessi a tuo nome.";
|
||||
"widget_integration_manager_disabled" = "Devi attivare il gestore di integrazioni nelle impostazioni";
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Carica widget";
|
||||
"room_widget_permission_creator_info_title" = "Questo widget è stato aggiunto da:";
|
||||
"room_widget_permission_webview_information_title" = "Usarlo potrebbe impostare cookie e condividere dati con %@:\n";
|
||||
"room_widget_permission_information_title" = "Usarlo potrebbe condividere dati con %@:\n";
|
||||
"room_widget_permission_display_name_permission" = "Il tuo nome visualizzato";
|
||||
"room_widget_permission_avatar_url_permission" = "Il tuo URL dell'avatar";
|
||||
"room_widget_permission_user_id_permission" = "Il tuo ID utente";
|
||||
"room_widget_permission_theme_permission" = "Il tuo tema";
|
||||
"room_widget_permission_widget_id_permission" = "ID widget";
|
||||
"room_widget_permission_room_id_permission" = "ID stanza";
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "checkbox";
|
||||
"widget_picker_manage_integrations" = "Gestisci integrazioni...";
|
||||
"service_terms_modal_policy_checkbox_accessibility_hint" = "Seleziona per accettare %@";
|
||||
"settings_labs_dm_key_verification" = "Verifica chiave via messaggio diretto";
|
||||
"settings_labs_cross_signing" = "Firma incrociata";
|
||||
|
|
|
@ -265,7 +265,7 @@
|
|||
"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_warning_about_encryption" = "종단간 암호화는 베타 버전이고 신뢰하지 못할 수 있습니다.\n\n아직 데이터를 보호한다고 신뢰하지 마세요.\n\n기기가 방에 참가하기 전에 아직 기록을 복호화할 수 없습니다.\n\n아직 암호화를 구현하지 않았기 때문에 암호화된 메시지는 클라이언트에 나타나지 않습니다.";
|
||||
"room_event_failed_to_send" = "보내기에 실패함";
|
||||
"room_action_camera" = "사진 또는 영상 찍기";
|
||||
"room_action_send_photo_or_video" = "사진 또는 영상 보내기";
|
||||
|
@ -629,7 +629,7 @@
|
|||
"deactivate_account_password_alert_message" = "계속하려면, 비밀번호를 입력해주세요";
|
||||
// Re-request confirmation dialog
|
||||
"rerequest_keys_alert_title" = "요청을 보냈습니다";
|
||||
"rerequest_keys_alert_message" = "메시지를 해독해서 이 기기로 키를 보낼 수 있도록 Riot을 다른 기기에 설치해주세요.";
|
||||
"rerequest_keys_alert_message" = "메시지를 복호화해서 이 기기로 키를 보낼 수 있도록 Riot을 다른 기기에 설치해주세요.";
|
||||
"key_backup_setup_title" = "키 백업";
|
||||
"key_backup_setup_skip_alert_title" = "확신합니까?";
|
||||
"key_backup_setup_skip_alert_message" = "로그아웃하거나 기기를 잃어버리면 보안 메시지를 잃게 됩니다.";
|
||||
|
@ -665,9 +665,9 @@
|
|||
"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_passphrase" = "이 암호로 백업을 복호화할 수 없습니다: 올바른 복구 암호를 입력해서 확인해주세요.";
|
||||
"key_backup_recover_invalid_recovery_key_title" = "복구 키가 맞지 않음";
|
||||
"key_backup_recover_invalid_recovery_key" = "이 키로 백업을 해독할 수 없습니다: 올바른 복구 키를 입력해서 확인해주세요.";
|
||||
"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" = "암호 입력";
|
||||
|
@ -889,3 +889,11 @@
|
|||
"settings_add_3pid_password_message" = "계속하려면 비밀번호를 입력해주세요";
|
||||
"settings_add_3pid_invalid_password_message" = "잘못된 비밀번호";
|
||||
"error_not_supported_on_mobile" = "%@ 모바일에서 할 수 없습니다.";
|
||||
"settings_integrations" = "통합";
|
||||
"settings_integrations_allow_button" = "통합 관리";
|
||||
"settings_integrations_allow_description" = "통합 관리자 (%@)를 사용해 봇, 브릿지, 위젯과 스티커 팩을 관리하세요.\n\n통합 관리자는 설정 데이터를 받고 위젯을 수정하거나, 방 초대를 보내고 권한 등급을 설정할 수 있습니다.";
|
||||
"widget_menu_refresh" = "새로고침";
|
||||
"widget_menu_open_outside" = "브라우저에서 열기";
|
||||
"widget_menu_revoke_permission" = "액세스 취소";
|
||||
"widget_menu_remove" = "모두를 위해 제거";
|
||||
"widget_integration_manager_disabled" = "설정에서 통합 관리자를 켜야 합니다";
|
||||
|
|
|
@ -850,3 +850,86 @@
|
|||
"contacts_address_book_no_identity_server" = "S’ka të formësuar shërbyes identitetesh";
|
||||
"settings_discovery_settings" = "ZBULIM";
|
||||
"settings_identity_server_settings" = "SHËRBYES IDENTITETESH";
|
||||
"settings_three_pids_management_information_part1" = "Administroni cilat adresa email apo numra telefonash mund të përdorni për të bërë hyrjen ose për të rimarrë llogarinë tuaj këtu. Kontrolloni cilët mund t’ju gjejnë ";
|
||||
"settings_three_pids_management_information_part2" = "Zbulim";
|
||||
"settings_three_pids_management_information_part3" = ".";
|
||||
"settings_add_3pid_password_title_email" = "Shtoni adresë email";
|
||||
"settings_add_3pid_password_title_msidsn" = "Shtoni numër telefoni";
|
||||
"settings_add_3pid_password_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin tuaj";
|
||||
"settings_add_3pid_invalid_password_message" = "Fjalëkalim i pavlefshëm";
|
||||
"settings_devices_description" = "Emri publik i një pajisjeje është i dukshëm për persona me të cilët komunikoni";
|
||||
"settings_discovery_no_identity_server" = "S’po përdorni ndonjë shërbyes identitetesh. Që të jeni i zbulueshëm nga kontakte ekzistuese që njihni, shtoni një të tillë.";
|
||||
"settings_discovery_terms_not_signed" = "Pajtohuni me Kushtet e Shërbimit të Shërbyesit të Identiteteve që t’i lejoni vetes të jeni i zbulueshëm përmes adrese email ose numri telefoni.";
|
||||
"settings_discovery_three_pids_management_information_part1" = "Administroni cilat adresa email ose numra telefonash mund të përdorin përdoruesit e tjerë për t’ju zbuluar dhe ftuar në dhoma. Shtoni ose hiqni prej kësaj liste adresa email ose numra telefonash ";
|
||||
"settings_discovery_three_pids_management_information_part2" = "Rregullime Përdoruesi";
|
||||
"settings_discovery_three_pids_management_information_part3" = ".";
|
||||
"settings_discovery_error_message" = "Ndodhi një gabim. Ju lutemi, riprovoni.";
|
||||
"settings_discovery_three_pid_details_title_email" = "Administroni email";
|
||||
"settings_discovery_three_pid_details_information_email" = "Administroni parapëlqime për këtë adresë email, të cilët përdorues të tjerë mund ta përdorin për t’ju zbuluar dhe ftuar në dhoma. Shtoni ose hiqni adresa email te Llogaritë.";
|
||||
"settings_discovery_three_pid_details_title_phone_number" = "Administroni numër telefoni";
|
||||
"settings_discovery_three_pid_details_information_phone_number" = "Administroni parapëlqime për këtë numër telefoni, të cilin mund ta përdorin përdorues të tjerë për t’ju zbuluar dhe ftuar në dhoma. Shtoni ose hiqni numra telefonash te Llogaritë.";
|
||||
"settings_discovery_three_pid_details_share_action" = "Ndajeni me të tjerë";
|
||||
"settings_discovery_three_pid_details_revoke_action" = "Shfuqizoje";
|
||||
"settings_discovery_three_pid_details_cancel_email_validation_action" = "Anuloni vlerësim email-i";
|
||||
"settings_discovery_three_pid_details_enter_sms_code_action" = "Jepni kod SMS aktivizimi";
|
||||
"settings_identity_server_description" = "Duke përdorur shërbyesin e identiteteve më sipër mund të zbuloni dhe të jeni i zbulueshëm nga kontakte ekzistuese që njihni.";
|
||||
"settings_identity_server_no_is" = "S’ka të formësuar shërbyes identitetesh";
|
||||
"settings_identity_server_no_is_description" = "S’po përdorni ndonjë shërbyes identitetesh. Që të zbuloni dhe të jeni i zbulueshëm nga kontakte ekzistuese që njihni, shtoni një më sipër.";
|
||||
// Identity server settings
|
||||
"identity_server_settings_title" = "Shërbyes Identitetesh";
|
||||
"identity_server_settings_description" = "Po përdorni %@ që të zbuloni dhe të jeni i zbulueshëm nga kontakte ekzistuese që dini.";
|
||||
"identity_server_settings_no_is_description" = "S’po përdorni ndonjë shërbyes identitetesh. Që të zbuloni dhe të jeni i zbulueshëm nga kontakte ekzistuese, shtoni një më sipër.";
|
||||
"identity_server_settings_place_holder" = "Jepni një shërbyes identitetesh";
|
||||
"identity_server_settings_add" = "Shtoje";
|
||||
"identity_server_settings_change" = "Ndryshojeni";
|
||||
"identity_server_settings_disconnect_info" = "Shkëputja nga shërbyesi juaj i identiteteve do të thotë se s’do të jeni të zbulueshëm nga përdorues të tjerë dhe as të jeni në gjendje të ftoni të tjerë përmes email-i ose telefoni.";
|
||||
"identity_server_settings_disconnect" = "Shkëputu";
|
||||
"identity_server_settings_alert_no_terms_title" = "Shërbyesi i identiteteve s’ka kushte shërbimi";
|
||||
"identity_server_settings_alert_no_terms" = "Shërbyesi i identiteteve që keni zgjedhur nuk ka ndonjë kusht shërbimesh. Vazhdoni vetëm nëse i zini besë të zotit të shërbyesit.";
|
||||
"identity_server_settings_alert_change_title" = "Ndryshoni shërbyes identitetesh";
|
||||
"identity_server_settings_alert_change" = "Të bëhet shkëputja nga shërbyesi i identiteteve %1$@ dhe të lidhet me %2$@?";
|
||||
"identity_server_settings_alert_disconnect_title" = "Shkëpute shërbyesin e identiteteve";
|
||||
"identity_server_settings_alert_disconnect" = "Të bëhet shkëputja nga shërbyesi i identiteteve %@?";
|
||||
"identity_server_settings_alert_disconnect_button" = "Shkëpute";
|
||||
"identity_server_settings_alert_disconnect_still_sharing_3pid" = "Ende ndani me të tjerët të dhëna tuajat personale në shërbyesin e identiteteve %@.\n\nKëshillojmë që të hiqni prej shërbyesit të identiteteve adresat tuaj email dhe numrat tuaj të telefonave përpara se të bëni shkëputjen.";
|
||||
"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "Shkëputu, sido qoftë";
|
||||
"identity_server_settings_alert_error_terms_not_accepted" = "Duhet të pranoni termat e %@ që ta caktoni si shërbyes identitetesh.";
|
||||
"identity_server_settings_alert_error_invalid_identity_server" = "%@ s’është shërbyes i vlershëm identitetesh.";
|
||||
"call_no_stun_server_error_title" = "Thirrja dështoi për shkak shërbyesi të keqformësuar";
|
||||
"call_no_stun_server_error_message_1" = "Që thirrjet të funksionojnë pa probleme, ju lutemi, kërkojini përgjegjësit të shërbyesit tuaj Home %@ të formësojë një shërbyes TURN.";
|
||||
"call_no_stun_server_error_message_2" = "Ndryshe, mund të provoni të përdorni shërbyesin publik te %@, por kjo s’do të jetë edhe aq e qëndrueshme, dhe adresa juaj IP do t’i bëhet e njohur atij shërbyesi. Këtë mund ta bëni edhe që nga Rregullimet";
|
||||
"call_no_stun_server_error_use_fallback_button" = "Provoni të përdorni %@";
|
||||
// Widget Picker
|
||||
"widget_picker_title" = "Integrime";
|
||||
"service_terms_modal_decline_button" = "Hidhe poshtë";
|
||||
"service_terms_modal_description_for_identity_server_1" = "Gjeni të tjerë përmes telefoni ose email-i";
|
||||
"service_terms_modal_description_for_identity_server_2" = "Bëhuni i gjetshëm përmes telefoni ose email-i";
|
||||
// Service terms - Variant for identity server when displayed out of a context
|
||||
"service_terms_modal_title_identity_server" = "Zbulim kontaktesh";
|
||||
"service_terms_modal_message_identity_server" = "Që të zbuloni kontakte, pranoni kushtet e shërbyesit të identiteteve (%@).";
|
||||
// Generic errors
|
||||
"error_invite_3pid_with_no_identity_server" = "Që të ftoni me email, shtoni një shërbyes identitetesh, që nga rregullimet tuaja.";
|
||||
"error_not_supported_on_mobile" = "Këtë s’mund ta bëni nga %@ për celular.";
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "kutizë";
|
||||
"settings_integrations" = "INTEGRIME";
|
||||
"settings_integrations_allow_button" = "Administroni integrime";
|
||||
"settings_integrations_allow_description" = "Përdorni një Përgjegjës Integrimesh (%@) që të administroni robotë, ura, widget-e dhe paketa ngjitësish.\n\nPërgjegjësit e Integrimeve marrin të dhëna formësimi dhe mund të ndryshojnë widget-e, të dërgojnë ftesa për në dhoma dhe të caktojnë shkallë pushteti në emrin tuaj.";
|
||||
"widget_menu_refresh" = "Rifreskoje";
|
||||
"widget_menu_open_outside" = "Hape në shfletues";
|
||||
"widget_menu_revoke_permission" = "Shfuqizo hyrje për mua";
|
||||
"widget_menu_remove" = "Hiqe për këdo";
|
||||
"widget_integration_manager_disabled" = "Lypset të aktivizoni Përgjegjës Integrimesh te rregullimet";
|
||||
"widget_picker_manage_integrations" = "Administroni integrime…";
|
||||
// Room widget permissions
|
||||
"room_widget_permission_title" = "Ngarko Widget";
|
||||
"room_widget_permission_creator_info_title" = "Ky <em>widget</em> qe shtuar nga:";
|
||||
"room_widget_permission_webview_information_title" = "Përdorimi i tij mund të sjellë depozitim <em>cookies</em> dhe ndarje të dhënash me %@:\n";
|
||||
"room_widget_permission_information_title" = "Përdorimi i tij mund të sjellë ndarje të dhënash me %@:\n";
|
||||
"room_widget_permission_display_name_permission" = "Emri juaj në ekran";
|
||||
"room_widget_permission_avatar_url_permission" = "URL-ja e avatarit tuaj";
|
||||
"room_widget_permission_user_id_permission" = "ID-ja juaj e përdoruesit";
|
||||
"room_widget_permission_theme_permission" = "Tema juaj";
|
||||
"room_widget_permission_widget_id_permission" = "ID Widget-i";
|
||||
"room_widget_permission_room_id_permission" = "ID Dhome";
|
||||
"service_terms_modal_policy_checkbox_accessibility_hint" = "I vini shenjë që të pranohet %@";
|
||||
|
|
29
Riot/Categories/FloatingPoint.swift
Normal file
29
Riot/Categories/FloatingPoint.swift
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
extension FloatingPoint {
|
||||
|
||||
/// Returns clamped `self` value.
|
||||
/// https://gist.github.com/laevandus/6fd35992157fcc9b5660bcbc82ebfb52#file-clampfloatingpoint-swift
|
||||
///
|
||||
/// - Parameter range: The closed range in which `self` should be clamped (`0.2...3.3` for example).
|
||||
/// - Returns: A FloatingPoint clamped value.
|
||||
func clamped(to range: ClosedRange<Self>) -> Self {
|
||||
return max(min(self, range.upperBound), range.lowerBound)
|
||||
}
|
||||
}
|
|
@ -29,4 +29,15 @@ extension UIButton {
|
|||
titleLabel.numberOfLines = 0
|
||||
titleLabel.textAlignment = textAlignment
|
||||
}
|
||||
|
||||
/// Set background color as an image.
|
||||
/// Useful to automatically adjust highlighted background if `adjustsImageWhenHighlighted` property is set to true or disabled background when `adjustsImageWhenDisabled`is set to true.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - color: The background color to set as an image.
|
||||
/// - state: The control state for wich to apply this color.
|
||||
func vc_setBackgroundColor(_ color: UIColor, for state: UIControl.State) {
|
||||
let image = UIImage.vc_image(from: color)
|
||||
self.setBackgroundImage(image, for: state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ internal enum Asset {
|
|||
internal static let adminIcon = ImageAsset(name: "admin_icon")
|
||||
internal static let backIcon = ImageAsset(name: "back_icon")
|
||||
internal static let chevron = ImageAsset(name: "chevron")
|
||||
internal static let closeButton = ImageAsset(name: "close_button")
|
||||
internal static let disclosureIcon = ImageAsset(name: "disclosure_icon")
|
||||
internal static let group = ImageAsset(name: "group")
|
||||
internal static let logo = ImageAsset(name: "logo")
|
||||
|
|
|
@ -117,6 +117,11 @@ internal enum StoryboardScene {
|
|||
|
||||
internal static let initialScene = InitialSceneType<Riot.TemplateScreenViewController>(storyboard: TemplateScreenViewController.self)
|
||||
}
|
||||
internal enum WidgetPermissionViewController: StoryboardType {
|
||||
internal static let storyboardName = "WidgetPermissionViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.WidgetPermissionViewController>(storyboard: WidgetPermissionViewController.self)
|
||||
}
|
||||
}
|
||||
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ internal enum VectorL10n {
|
|||
internal static var accept: String {
|
||||
return VectorL10n.tr("Vector", "accept")
|
||||
}
|
||||
/// checkbox
|
||||
internal static var accessibilityCheckboxLabel: String {
|
||||
return VectorL10n.tr("Vector", "accessibility_checkbox_label")
|
||||
}
|
||||
/// Logout all accounts
|
||||
internal static var accountLogoutAll: String {
|
||||
return VectorL10n.tr("Vector", "account_logout_all")
|
||||
|
@ -2474,6 +2478,46 @@ internal enum VectorL10n {
|
|||
internal static var roomWarningAboutEncryption: String {
|
||||
return VectorL10n.tr("Vector", "room_warning_about_encryption")
|
||||
}
|
||||
/// Your avatar URL
|
||||
internal static var roomWidgetPermissionAvatarUrlPermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_avatar_url_permission")
|
||||
}
|
||||
/// This widget was added by:
|
||||
internal static var roomWidgetPermissionCreatorInfoTitle: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_creator_info_title")
|
||||
}
|
||||
/// Your display name
|
||||
internal static var roomWidgetPermissionDisplayNamePermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_display_name_permission")
|
||||
}
|
||||
/// Using it may share data with %@:\n
|
||||
internal static func roomWidgetPermissionInformationTitle(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_information_title", p1)
|
||||
}
|
||||
/// Room ID
|
||||
internal static var roomWidgetPermissionRoomIdPermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_room_id_permission")
|
||||
}
|
||||
/// Your theme
|
||||
internal static var roomWidgetPermissionThemePermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_theme_permission")
|
||||
}
|
||||
/// Load Widget
|
||||
internal static var roomWidgetPermissionTitle: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_title")
|
||||
}
|
||||
/// Your user ID
|
||||
internal static var roomWidgetPermissionUserIdPermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_user_id_permission")
|
||||
}
|
||||
/// Using it may set cookies and share data with %@:\n
|
||||
internal static func roomWidgetPermissionWebviewInformationTitle(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_webview_information_title", p1)
|
||||
}
|
||||
/// Widget ID
|
||||
internal static var roomWidgetPermissionWidgetIdPermission: String {
|
||||
return VectorL10n.tr("Vector", "room_widget_permission_widget_id_permission")
|
||||
}
|
||||
/// Save
|
||||
internal static var save: String {
|
||||
return VectorL10n.tr("Vector", "save")
|
||||
|
@ -2546,6 +2590,10 @@ internal enum VectorL10n {
|
|||
internal static func serviceTermsModalMessageIdentityServer(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "service_terms_modal_message_identity_server", p1)
|
||||
}
|
||||
/// Check to accept %@
|
||||
internal static func serviceTermsModalPolicyCheckboxAccessibilityHint(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "service_terms_modal_policy_checkbox_accessibility_hint", p1)
|
||||
}
|
||||
/// Terms Of Service
|
||||
internal static var serviceTermsModalTitle: String {
|
||||
return VectorL10n.tr("Vector", "service_terms_modal_title")
|
||||
|
@ -2810,6 +2858,18 @@ internal enum VectorL10n {
|
|||
internal static var settingsIgnoredUsers: String {
|
||||
return VectorL10n.tr("Vector", "settings_ignored_users")
|
||||
}
|
||||
/// INTEGRATIONS
|
||||
internal static var settingsIntegrations: String {
|
||||
return VectorL10n.tr("Vector", "settings_integrations")
|
||||
}
|
||||
/// Manage integrations
|
||||
internal static var settingsIntegrationsAllowButton: String {
|
||||
return VectorL10n.tr("Vector", "settings_integrations_allow_button")
|
||||
}
|
||||
/// Use an Integration Manager (%@) to manage bots, bridges, widgets and sticker packs.\n\nIntegration Managers receive configuration data, and can modify widgets, send room invites and set power levels on your behalf.
|
||||
internal static func settingsIntegrationsAllowDescription(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "settings_integrations_allow_description", p1)
|
||||
}
|
||||
/// KEY BACKUP
|
||||
internal static var settingsKeyBackup: String {
|
||||
return VectorL10n.tr("Vector", "settings_key_backup")
|
||||
|
@ -2910,6 +2970,14 @@ internal enum VectorL10n {
|
|||
internal static var settingsLabsCreateConferenceWithJitsi: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_create_conference_with_jitsi")
|
||||
}
|
||||
/// Cross-Signing
|
||||
internal static var settingsLabsCrossSigning: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_cross_signing")
|
||||
}
|
||||
/// Key verification by direct message
|
||||
internal static var settingsLabsDmKeyVerification: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_dm_key_verification")
|
||||
}
|
||||
/// End-to-End Encryption
|
||||
internal static var settingsLabsE2eEncryption: String {
|
||||
return VectorL10n.tr("Vector", "settings_labs_e2e_encryption")
|
||||
|
@ -3250,6 +3318,10 @@ internal enum VectorL10n {
|
|||
internal static var widgetIntegrationFailedToSendRequest: String {
|
||||
return VectorL10n.tr("Vector", "widget_integration_failed_to_send_request")
|
||||
}
|
||||
/// You need to enable Integration Manager in settings
|
||||
internal static var widgetIntegrationManagerDisabled: String {
|
||||
return VectorL10n.tr("Vector", "widget_integration_manager_disabled")
|
||||
}
|
||||
/// Missing room_id in request.
|
||||
internal static var widgetIntegrationMissingRoomId: String {
|
||||
return VectorL10n.tr("Vector", "widget_integration_missing_room_id")
|
||||
|
@ -3290,6 +3362,22 @@ internal enum VectorL10n {
|
|||
internal static var widgetIntegrationsServerFailedToConnect: String {
|
||||
return VectorL10n.tr("Vector", "widget_integrations_server_failed_to_connect")
|
||||
}
|
||||
/// Open in browser
|
||||
internal static var widgetMenuOpenOutside: String {
|
||||
return VectorL10n.tr("Vector", "widget_menu_open_outside")
|
||||
}
|
||||
/// Refresh
|
||||
internal static var widgetMenuRefresh: String {
|
||||
return VectorL10n.tr("Vector", "widget_menu_refresh")
|
||||
}
|
||||
/// Remove for everyone
|
||||
internal static var widgetMenuRemove: String {
|
||||
return VectorL10n.tr("Vector", "widget_menu_remove")
|
||||
}
|
||||
/// Revoke access for me
|
||||
internal static var widgetMenuRevokePermission: String {
|
||||
return VectorL10n.tr("Vector", "widget_menu_revoke_permission")
|
||||
}
|
||||
/// No integrations server configured
|
||||
internal static var widgetNoIntegrationsServerConfigured: String {
|
||||
return VectorL10n.tr("Vector", "widget_no_integrations_server_configured")
|
||||
|
@ -3298,6 +3386,10 @@ internal enum VectorL10n {
|
|||
internal static var widgetNoPowerToManage: String {
|
||||
return VectorL10n.tr("Vector", "widget_no_power_to_manage")
|
||||
}
|
||||
/// Manage integrations...
|
||||
internal static var widgetPickerManageIntegrations: String {
|
||||
return VectorL10n.tr("Vector", "widget_picker_manage_integrations")
|
||||
}
|
||||
/// Integrations
|
||||
internal static var widgetPickerTitle: String {
|
||||
return VectorL10n.tr("Vector", "widget_picker_title")
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc enum RoomMessageURLType: Int {
|
||||
case appleDataDetector
|
||||
case http
|
||||
case dummy
|
||||
case unknown
|
||||
}
|
||||
|
||||
/// URL parser for room messages.
|
||||
@objcMembers
|
||||
final class RoomMessageURLParser: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Scheme {
|
||||
static let appleDataDetector = "x-apple-data-detectors"
|
||||
static let http = "http"
|
||||
static let https = "https"
|
||||
}
|
||||
|
||||
private enum Constants {
|
||||
static let dummyURL = "#"
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func parseURL(_ url: URL) -> RoomMessageURLType {
|
||||
|
||||
let roomMessageLink: RoomMessageURLType
|
||||
|
||||
if let scheme = url.scheme?.lowercased() {
|
||||
switch scheme {
|
||||
case Scheme.appleDataDetector:
|
||||
roomMessageLink = .appleDataDetector
|
||||
case Scheme.http, Scheme.https:
|
||||
roomMessageLink = .http
|
||||
default:
|
||||
roomMessageLink = .unknown
|
||||
}
|
||||
} else if url.absoluteString == Constants.dummyURL {
|
||||
roomMessageLink = .dummy
|
||||
} else {
|
||||
roomMessageLink = .unknown
|
||||
}
|
||||
|
||||
return roomMessageLink
|
||||
}
|
||||
}
|
|
@ -28,6 +28,18 @@ final class SerializationService: SerializationServiceType {
|
|||
func deserialize<T: Decodable>(_ data: Data) throws -> T {
|
||||
return try decoder.decode(T.self, from: data)
|
||||
}
|
||||
|
||||
func deserialize<T: Decodable>(_ object: Any) throws -> T {
|
||||
let jsonData: Data
|
||||
|
||||
if let data = object as? Data {
|
||||
jsonData = data
|
||||
} else {
|
||||
jsonData = try JSONSerialization.data(withJSONObject: object, options: [])
|
||||
}
|
||||
return try decoder.decode(T.self, from: jsonData)
|
||||
}
|
||||
|
||||
|
||||
func serialize<T: Encodable>(_ object: T) throws -> Data {
|
||||
return try encoder.encode(object)
|
||||
|
|
|
@ -18,5 +18,7 @@ import Foundation
|
|||
|
||||
protocol SerializationServiceType {
|
||||
func deserialize<T: Decodable>(_ data: Data) throws -> T
|
||||
func deserialize<T: Decodable>(_ object: Any) throws -> T
|
||||
|
||||
func serialize<T: Encodable>(_ object: T) throws -> Data
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ final class RiotSettings: NSObject {
|
|||
static let pinRoomsWithUnreadMessages = "pinRoomsWithUnread"
|
||||
static let allowStunServerFallback = "allowStunServerFallback"
|
||||
static let stunServerFallback = "stunServerFallback"
|
||||
static let enableCrossSigning = "enableCrossSigning"
|
||||
static let enableDMKeyVerification = "enableDMKeyVerification"
|
||||
}
|
||||
|
||||
/// Riot Standard Room Member Power Level
|
||||
|
@ -121,7 +123,22 @@ final class RiotSettings: NSObject {
|
|||
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
|
||||
}
|
||||
}
|
||||
|
||||
var enableDMKeyVerification: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableDMKeyVerification)
|
||||
} set {
|
||||
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableDMKeyVerification)
|
||||
}
|
||||
}
|
||||
|
||||
var enableCrossSigning: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableCrossSigning)
|
||||
} set {
|
||||
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableCrossSigning)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Calls
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Model for "im.vector.setting.allowed_widgets"
|
||||
/// https://github.com/vector-im/riot-meta/blob/master/spec/settings.md#tracking-which-widgets-the-user-has-allowed-to-load
|
||||
struct RiotSettingAllowedWidgets {
|
||||
let widgets: [String: Bool]
|
||||
|
||||
// Widget type -> Server domain -> Bool
|
||||
let nativeWidgets: [String: [String: Bool]]
|
||||
}
|
||||
|
||||
extension RiotSettingAllowedWidgets: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case widgets
|
||||
case nativeWidgets = "native_widgets"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/// Model for "im.vector.setting.integration_provisioning"
|
||||
/// https://github.com/vector-im/riot-meta/blob/master/spec/settings.md#selecting-no-provisioning-for-integration-managers
|
||||
struct RiotSettingIntegrationProvisioning {
|
||||
let enabled: Bool
|
||||
}
|
||||
|
||||
extension RiotSettingIntegrationProvisioning: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case enabled
|
||||
}
|
||||
}
|
219
Riot/Managers/Settings/Shared/RiotSharedSettings.swift
Normal file
219
Riot/Managers/Settings/Shared/RiotSharedSettings.swift
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
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 MatrixSDK
|
||||
|
||||
@objc enum WidgetPermission: Int {
|
||||
case undefined
|
||||
case granted
|
||||
case declined
|
||||
}
|
||||
|
||||
/// Shared user settings across all Riot clients.
|
||||
/// It implements https://github.com/vector-im/riot-meta/blob/master/spec/settings.md
|
||||
@objcMembers
|
||||
class RiotSharedSettings: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
private enum Settings {
|
||||
static let breadcrumbs = "im.vector.setting.breadcrumbs"
|
||||
static let integrationProvisioning = "im.vector.setting.integration_provisioning"
|
||||
static let allowedWidgets = "im.vector.setting.allowed_widgets"
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Properties
|
||||
// MARK: Private
|
||||
private let session: MXSession
|
||||
private lazy var serializationService: SerializationServiceType = SerializationService()
|
||||
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
// MARK: Integration provisioning
|
||||
|
||||
var hasIntegrationProvisioningEnabled: Bool {
|
||||
return getIntegrationProvisioning()?.enabled ?? true
|
||||
}
|
||||
|
||||
func getIntegrationProvisioning() -> RiotSettingIntegrationProvisioning? {
|
||||
guard let integrationProvisioningDict = getAccountData(forEventType: Settings.integrationProvisioning) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return try? serializationService.deserialize(integrationProvisioningDict)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setIntegrationProvisioning(enabled: Bool,
|
||||
success: @escaping () -> Void,
|
||||
failure: @escaping (Error?) -> Void)
|
||||
-> MXHTTPOperation? {
|
||||
|
||||
// Update only the "widgets" field in the account data
|
||||
var integrationProvisioningDict = getAccountData(forEventType: Settings.integrationProvisioning) ?? [:]
|
||||
integrationProvisioningDict[RiotSettingIntegrationProvisioning.CodingKeys.enabled.rawValue] = enabled
|
||||
|
||||
return session.setAccountData(integrationProvisioningDict, forType: Settings.integrationProvisioning, success: success, failure: failure)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Allowed widgets
|
||||
func permission(for widget: Widget) -> WidgetPermission {
|
||||
guard let allowedWidgets = getAllowedWidgets() else {
|
||||
return .undefined
|
||||
}
|
||||
|
||||
if let value = allowedWidgets.widgets[widget.widgetEvent.eventId] {
|
||||
return value == true ? .granted : .declined
|
||||
} else {
|
||||
return .undefined
|
||||
}
|
||||
}
|
||||
|
||||
func getAllowedWidgets() -> RiotSettingAllowedWidgets? {
|
||||
guard let allowedWidgetsDict = getAccountData(forEventType: Settings.allowedWidgets) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return try? serializationService.deserialize(allowedWidgetsDict)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setPermission(_ permission: WidgetPermission,
|
||||
for widget: Widget,
|
||||
success: @escaping () -> Void,
|
||||
failure: @escaping (Error?) -> Void)
|
||||
-> MXHTTPOperation? {
|
||||
|
||||
guard let widgetEventId = widget.widgetEvent.eventId else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var widgets = getAllowedWidgets()?.widgets ?? [:]
|
||||
|
||||
switch permission {
|
||||
case .undefined:
|
||||
widgets.removeValue(forKey: widgetEventId)
|
||||
case .granted:
|
||||
widgets[widgetEventId] = true
|
||||
case .declined:
|
||||
widgets[widgetEventId] = false
|
||||
}
|
||||
|
||||
// Update only the "widgets" field in the account data
|
||||
var allowedWidgetsDict = getAccountData(forEventType: Settings.allowedWidgets) ?? [:]
|
||||
allowedWidgetsDict[RiotSettingAllowedWidgets.CodingKeys.widgets.rawValue] = widgets
|
||||
|
||||
return session.setAccountData(allowedWidgetsDict, forType: Settings.allowedWidgets, success: success, failure: failure)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Allowed native widgets
|
||||
|
||||
/// Get the permission for widget that will be displayed natively instead within
|
||||
/// a webview.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - widget: the widget
|
||||
/// - url: the url the native implementation will open. Nil will use the url declared in the widget
|
||||
/// - Returns: the permission
|
||||
func permission(forNative widget: Widget, fromUrl url: URL? = nil) -> WidgetPermission {
|
||||
guard let allowedWidgets = getAllowedWidgets() else {
|
||||
return .undefined
|
||||
}
|
||||
|
||||
guard let type = widget.type, let domain = domainForNativeWidget(widget, fromUrl: url) else {
|
||||
return .undefined
|
||||
}
|
||||
|
||||
if let value = allowedWidgets.nativeWidgets[type]?[domain] {
|
||||
return value == true ? .granted : .declined
|
||||
} else {
|
||||
return .undefined
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the permission for widget that is displayed natively.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - permission: the permission to set
|
||||
/// - widget: the widget
|
||||
/// - url: the url the native implementation opens. Nil will use the url declared in the widget
|
||||
/// - success: the success block
|
||||
/// - failure: the failure block
|
||||
/// - Returns: a `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func setPermission(_ permission: WidgetPermission,
|
||||
forNative widget: Widget,
|
||||
fromUrl url: URL?,
|
||||
success: @escaping () -> Void,
|
||||
failure: @escaping (Error?) -> Void)
|
||||
-> MXHTTPOperation? {
|
||||
|
||||
guard let type = widget.type, let domain = domainForNativeWidget(widget, fromUrl: url) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var nativeWidgets = getAllowedWidgets()?.nativeWidgets ?? [String: [String: Bool]]()
|
||||
var nativeWidgetsType = nativeWidgets[type] ?? [String: Bool]()
|
||||
|
||||
switch permission {
|
||||
case .undefined:
|
||||
nativeWidgetsType.removeValue(forKey: domain)
|
||||
case .granted:
|
||||
nativeWidgetsType[domain] = true
|
||||
case .declined:
|
||||
nativeWidgetsType[domain] = false
|
||||
}
|
||||
|
||||
nativeWidgets[type] = nativeWidgetsType
|
||||
|
||||
// Update only the "native_widgets" field in the account data
|
||||
var allowedWidgetsDict = getAccountData(forEventType: Settings.allowedWidgets) ?? [:]
|
||||
allowedWidgetsDict[RiotSettingAllowedWidgets.CodingKeys.nativeWidgets.rawValue] = nativeWidgets
|
||||
|
||||
return session.setAccountData(allowedWidgetsDict, forType: Settings.allowedWidgets, success: success, failure: failure)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
private func getAccountData(forEventType eventType: String) -> [String: Any]? {
|
||||
return session.accountData.accountData(forEventType: eventType) as? [String: Any]
|
||||
}
|
||||
|
||||
private func domainForNativeWidget(_ widget: Widget, fromUrl url: URL? = nil) -> String? {
|
||||
var widgetUrl: URL?
|
||||
if let widgetUrlString = widget.url {
|
||||
widgetUrl = URL(string: widgetUrlString)
|
||||
}
|
||||
|
||||
guard let url = url ?? widgetUrl, let domain = url.host else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return domain
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@
|
|||
// Check if their scalar token must added
|
||||
if ([[WidgetManager sharedManager] isScalarUrl:widgetUrl forUser:userId])
|
||||
{
|
||||
return [[WidgetManager sharedManager] getScalarTokenForMXSession:_mxSession validate:NO success:^(NSString *scalarToken) {
|
||||
return [[WidgetManager sharedManager] getScalarTokenForMXSession:_mxSession validate:YES success:^(NSString *scalarToken) {
|
||||
// Add the user scalar token
|
||||
widgetUrl = [widgetUrl stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@",
|
||||
scalarToken]];
|
||||
|
|
|
@ -56,6 +56,7 @@ typedef enum : NSUInteger
|
|||
WidgetManagerErrorCodeNotEnoughPower,
|
||||
WidgetManagerErrorCodeCreationFailed,
|
||||
WidgetManagerErrorCodeNoIntegrationsServerConfigured,
|
||||
WidgetManagerErrorCodeDisabledIntegrationsServer,
|
||||
WidgetManagerErrorCodeFailedToConnectToIntegrationsServer,
|
||||
WidgetManagerErrorCodeTermsNotSigned
|
||||
}
|
||||
|
|
|
@ -50,6 +50,9 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
|
||||
// User id -> scalar token
|
||||
NSMutableDictionary<NSString*, WidgetManagerConfig*> *configs;
|
||||
|
||||
// User id -> MXSession
|
||||
NSMutableDictionary<NSString*, MXSession*> *matrixSessions;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -73,6 +76,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
matrixSessions = [NSMutableDictionary dictionary];
|
||||
widgetEventListener = [NSMutableDictionary dictionary];
|
||||
successBlockForWidgetCreation = [NSMutableDictionary dictionary];
|
||||
failureBlockForWidgetCreation = [NSMutableDictionary dictionary];
|
||||
|
@ -265,6 +269,14 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
return nil;
|
||||
}
|
||||
|
||||
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:room.mxSession];
|
||||
if (!sharedSettings.hasIntegrationProvisioningEnabled)
|
||||
{
|
||||
NSLog(@"[WidgetManager] createJitsiWidgetInRoom: Error: Disabled integration manager for user %@", userId);
|
||||
failure(self.errorForDisabledIntegrationManager);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Build data for a jitsi widget
|
||||
NSString *widgetId = [NSString stringWithFormat:@"%@_%@_%@", kWidgetTypeJitsi, room.mxSession.myUser.userId, @((uint64_t)([[NSDate date] timeIntervalSince1970] * 1000))];
|
||||
|
||||
|
@ -367,6 +379,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
{
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
|
||||
matrixSessions[mxSession.matrixRestClient.credentials.userId] = mxSession;
|
||||
|
||||
NSString *hash = [NSString stringWithFormat:@"%p", mxSession];
|
||||
|
||||
id listener = [mxSession listenToEventsOfTypes:@[kWidgetMatrixEventTypeString, kWidgetModularEventTypeString] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
|
||||
|
@ -421,6 +435,12 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
|
||||
- (void)removeMatrixSession:(MXSession *)mxSession
|
||||
{
|
||||
// Remove by value in a dict
|
||||
for (NSString *key in [matrixSessions allKeysForObject:mxSession])
|
||||
{
|
||||
[matrixSessions removeObjectForKey:key];
|
||||
}
|
||||
|
||||
// mxSession.myUser.userId and mxSession.matrixRestClient.credentials.userId may be nil here
|
||||
// So, use a kind of hash value instead
|
||||
NSString *hash = [NSString stringWithFormat:@"%p", mxSession];
|
||||
|
@ -433,18 +453,59 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
[failureBlockForWidgetCreation removeObjectForKey:hash];
|
||||
}
|
||||
|
||||
- (MXSession*)matrixSessionForUser:(NSString*)userId
|
||||
{
|
||||
return matrixSessions[userId];
|
||||
}
|
||||
|
||||
- (void)deleteDataForUser:(NSString *)userId
|
||||
{
|
||||
[configs removeObjectForKey:userId];
|
||||
[self saveConfigs];
|
||||
}
|
||||
|
||||
#pragma mark - User integrations configuration
|
||||
|
||||
- (WidgetManagerConfig*)createWidgetManagerConfigForUser:(NSString*)userId
|
||||
{
|
||||
WidgetManagerConfig *config;
|
||||
|
||||
MXSession *session = [self matrixSessionForUser:userId];
|
||||
|
||||
// Find the integrations settings for the user
|
||||
|
||||
// First, look at matrix account
|
||||
// TODO in another user story
|
||||
|
||||
// Then, try to the homeserver configuration
|
||||
MXWellknownIntegrationsManager *integrationsManager = session.homeserverWellknown.integrations.managers.firstObject;
|
||||
if (integrationsManager)
|
||||
{
|
||||
config = [[WidgetManagerConfig alloc] initWithApiUrl:integrationsManager.apiUrl uiUrl:integrationsManager.uiUrl];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback on app settings
|
||||
config = [self createWidgetManagerConfigWithAppSettings];
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
- (WidgetManagerConfig*)createWidgetManagerConfigWithAppSettings
|
||||
{
|
||||
NSString *apiUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"];
|
||||
NSString *uiUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsUiUrl"];
|
||||
|
||||
return [[WidgetManagerConfig alloc] initWithApiUrl:apiUrl uiUrl:uiUrl];
|
||||
}
|
||||
|
||||
#pragma mark - Modular interface
|
||||
|
||||
- (WidgetManagerConfig*)configForUser:(NSString*)userId
|
||||
{
|
||||
// Return a default config by default
|
||||
return configs[userId] ? configs[userId] : [WidgetManagerConfig new];
|
||||
return configs[userId] ? configs[userId] : [self createWidgetManagerConfigForUser:userId];
|
||||
}
|
||||
|
||||
- (BOOL)hasIntegrationManagerForUser:(NSString*)userId
|
||||
|
@ -697,7 +758,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
|
||||
NSLog(@"[WidgetManager] migrate scalarTokens to integrationManagerConfigs for %@", userId);
|
||||
|
||||
WidgetManagerConfig *config = [WidgetManagerConfig new];
|
||||
WidgetManagerConfig *config = [self createWidgetManagerConfigWithAppSettings];
|
||||
config.scalarToken = scalarToken;
|
||||
|
||||
configs[userId] = config;
|
||||
|
@ -738,4 +799,11 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
|||
userInfo:@{NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_no_integrations_server_configured", @"Vector", nil)}];
|
||||
}
|
||||
|
||||
- (NSError*)errorForDisabledIntegrationManager
|
||||
{
|
||||
return [NSError errorWithDomain:WidgetManagerErrorDomain
|
||||
code:WidgetManagerErrorCodeDisabledIntegrationsServer
|
||||
userInfo:@{NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_integration_manager_disabled", @"Vector", nil)}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -75,15 +75,6 @@ class WidgetManagerConfig: NSObject, NSCoding {
|
|||
super.init()
|
||||
}
|
||||
|
||||
override convenience init () {
|
||||
// Use app settings as default
|
||||
let apiUrl = UserDefaults.standard.object(forKey: "integrationsRestUrl") as? NSString
|
||||
let uiUrl = UserDefaults.standard.object(forKey: "integrationsUiUrl") as? NSString
|
||||
|
||||
self.init(apiUrl: apiUrl, uiUrl: uiUrl)
|
||||
}
|
||||
|
||||
|
||||
/// MARK: - NSCoding
|
||||
|
||||
enum CodingKeys: String {
|
||||
|
|
|
@ -53,6 +53,9 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
// }
|
||||
|
||||
func present(from viewController: UIViewController, otherUserId: String, otherDeviceId: String, animated: Bool) {
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Present from \(viewController)")
|
||||
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
|
@ -62,6 +65,9 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
}
|
||||
|
||||
func present(from viewController: UIViewController, incomingTransaction: MXIncomingSASTransaction, animated: Bool) {
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Present incoming verification from \(viewController)")
|
||||
|
||||
let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, incomingTransaction: incomingTransaction)
|
||||
deviceVerificationCoordinator.delegate = self
|
||||
viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
|
@ -73,7 +79,10 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject {
|
|||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
NSLog("[DeviceVerificationCoordinatorBridgePresenter] Dismiss")
|
||||
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import Foundation
|
|||
|
||||
/// DeviceVerificationIncomingViewController view actions exposed to view model
|
||||
enum DeviceVerificationIncomingViewAction {
|
||||
case loadData
|
||||
case accept
|
||||
case cancel
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ final class DeviceVerificationIncomingViewController: UIViewController {
|
|||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
|
|
@ -50,8 +50,6 @@ final class DeviceVerificationIncomingViewModel: DeviceVerificationIncomingViewM
|
|||
self.deviceId = transaction.otherDeviceId
|
||||
|
||||
self.mediaManager = session.mediaManager
|
||||
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -61,6 +59,8 @@ final class DeviceVerificationIncomingViewModel: DeviceVerificationIncomingViewM
|
|||
|
||||
func process(viewAction: DeviceVerificationIncomingViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
case .accept:
|
||||
self.acceptIncomingDeviceVerification()
|
||||
case .cancel:
|
||||
|
@ -89,6 +89,10 @@ final class DeviceVerificationIncomingViewModel: DeviceVerificationIncomingViewM
|
|||
private func registerTransactionDidStateChangeNotification(transaction: MXIncomingSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
private func unregisterTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXDeviceVerificationTransactionDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXIncomingSASTransaction else {
|
||||
|
@ -97,17 +101,20 @@ final class DeviceVerificationIncomingViewModel: DeviceVerificationIncomingViewM
|
|||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .loaded)
|
||||
self.coordinatorDelegate?.deviceVerificationIncomingViewModel(self, didAcceptTransaction: self.transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
|
|
|
@ -115,21 +115,22 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
|
|||
guard let transaction = notification.object as? MXOutgoingSASTransaction else {
|
||||
return
|
||||
}
|
||||
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
|
|
|
@ -20,6 +20,7 @@ import Foundation
|
|||
|
||||
/// DeviceVerificationVerifyViewController view actions exposed to view model
|
||||
enum DeviceVerificationVerifyViewAction {
|
||||
case loadData
|
||||
case confirm
|
||||
case complete
|
||||
case cancel
|
||||
|
|
|
@ -70,6 +70,7 @@ final class DeviceVerificationVerifyViewController: UIViewController {
|
|||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
|
@ -41,8 +41,6 @@ final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModel
|
|||
self.transaction = transaction
|
||||
self.emojis = self.transaction.sasEmoji
|
||||
self.decimal = self.transaction.sasDecimal
|
||||
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -52,6 +50,8 @@ final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModel
|
|||
|
||||
func process(viewAction: DeviceVerificationVerifyViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.registerTransactionDidStateChangeNotification(transaction: transaction)
|
||||
case .confirm:
|
||||
self.confirmTransaction()
|
||||
case .complete:
|
||||
|
@ -83,6 +83,10 @@ final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModel
|
|||
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction)
|
||||
}
|
||||
|
||||
private func unregisterTransactionDidStateChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXDeviceVerificationTransactionDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func transactionDidStateChange(notification: Notification) {
|
||||
guard let transaction = notification.object as? MXSASTransaction else {
|
||||
|
@ -91,27 +95,26 @@ final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModel
|
|||
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateVerified:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .loaded)
|
||||
self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidComplete(self)
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateError:
|
||||
guard let error = transaction.error else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .error(error))
|
||||
case MXSASTransactionStateCancelled:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXSASTransactionStateCancelledByMe:
|
||||
guard let reason = transaction.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
default:
|
||||
break
|
||||
|
|
|
@ -38,6 +38,7 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
|
|||
}
|
||||
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
@property (nonatomic) BOOL isViewAppearedOnce;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -69,15 +70,26 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
|
|||
operation = nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self loadData];
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (!self.isViewAppearedOnce)
|
||||
{
|
||||
self.isViewAppearedOnce = YES;
|
||||
[self loadData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadData
|
||||
{
|
||||
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:mxSession];
|
||||
if (!sharedSettings.hasIntegrationProvisioningEnabled)
|
||||
{
|
||||
[self showDisabledIntegrationManagerError];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.URL && !operation)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
|
@ -697,6 +709,33 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
|
|||
}
|
||||
|
||||
|
||||
#pragma mark - Widget Permission
|
||||
|
||||
- (void)checkWidgetPermissionWithCompletion:(void (^)(BOOL granted))completion
|
||||
{
|
||||
// The integration manager widget has its own terms
|
||||
completion(YES);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Disabled Integrations
|
||||
|
||||
- (void)showDisabledIntegrationManagerError
|
||||
{
|
||||
NSString *message = NSLocalizedStringFromTable(@"widget_integration_manager_disabled", @"Vector", nil);
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}]];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Service terms
|
||||
|
||||
- (void)presentTerms
|
||||
|
@ -732,4 +771,12 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
|
|||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter session:(MXSession * _Nonnull)session
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<?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="3Nb-ba-XcY">
|
||||
<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>
|
||||
<!--Widget Permission View Controller-->
|
||||
<scene sceneID="hVk-V5-GY4">
|
||||
<objects>
|
||||
<viewController id="3Nb-ba-XcY" customClass="WidgetPermissionViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Z4L-Ka-Saj">
|
||||
<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="yPm-GN-iNf">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vsY-4j-uQz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="421.5"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F2c-As-8Ce">
|
||||
<rect key="frame" x="364" y="20" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="g3s-jh-fkQ"/>
|
||||
<constraint firstAttribute="height" constant="30" id="qvd-zA-8uA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" image="close_button"/>
|
||||
<connections>
|
||||
<action selector="closeButtonAction:" destination="3Nb-ba-XcY" eventType="touchUpInside" id="qJ1-fx-suE"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Load Widget" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4jF-pR-0xn">
|
||||
<rect key="frame" x="20" y="24.5" width="334" height="21.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This widget was added by:" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iUc-92-5Iv">
|
||||
<rect key="frame" x="20" y="64" width="374" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i5q-8U-NpS">
|
||||
<rect key="frame" x="20" y="102" width="374" height="54"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UvZ-W0-l9O" customClass="MXKImageView">
|
||||
<rect key="frame" x="0.0" y="7" width="40" height="40"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="CsC-a2-xHm"/>
|
||||
<constraint firstAttribute="width" constant="40" id="f9t-fq-fc8"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="niK-55-mG3">
|
||||
<rect key="frame" x="50" y="0.0" width="324" height="54"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="azre azer azer azer azer aezr azer azer zaer ae" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FjU-fb-8u1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="324" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@paul:matrix.org" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Y8T-5r-qPI">
|
||||
<rect key="frame" x="0.0" y="36" width="324" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="UvZ-W0-l9O" firstAttribute="leading" secondItem="i5q-8U-NpS" secondAttribute="leading" id="2Os-M9-Pld"/>
|
||||
<constraint firstItem="niK-55-mG3" firstAttribute="top" relation="greaterThanOrEqual" secondItem="i5q-8U-NpS" secondAttribute="top" id="5NQ-1c-JIi"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="UvZ-W0-l9O" secondAttribute="bottom" id="LhM-Ik-Cag"/>
|
||||
<constraint firstAttribute="height" priority="250" id="U78-dc-mnC"/>
|
||||
<constraint firstItem="niK-55-mG3" firstAttribute="leading" secondItem="UvZ-W0-l9O" secondAttribute="trailing" constant="10" id="VUY-T5-xMd"/>
|
||||
<constraint firstItem="UvZ-W0-l9O" firstAttribute="top" relation="greaterThanOrEqual" secondItem="i5q-8U-NpS" secondAttribute="top" id="Whs-DN-c3I"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="niK-55-mG3" secondAttribute="bottom" id="m6M-rS-dt4"/>
|
||||
<constraint firstItem="UvZ-W0-l9O" firstAttribute="centerY" secondItem="i5q-8U-NpS" secondAttribute="centerY" id="n5u-UV-Cw6"/>
|
||||
<constraint firstItem="niK-55-mG3" firstAttribute="centerY" secondItem="UvZ-W0-l9O" secondAttribute="centerY" id="qLj-Te-QyY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="niK-55-mG3" secondAttribute="trailing" id="qeG-IV-RJa"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d3k-sH-pkd">
|
||||
<rect key="frame" x="20" y="176" width="374" height="161.5"/>
|
||||
<string key="text">Using it may set cookies and share data with widget.com:
|
||||
|
||||
• Your display name
|
||||
• Your avatar URL
|
||||
• Your user ID
|
||||
• Your theme
|
||||
• Room ID
|
||||
• Widget ID</string>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0mL-Yj-ueq">
|
||||
<rect key="frame" x="20" y="357.5" width="374" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="e3O-dh-ock"/>
|
||||
<constraint firstAttribute="height" constant="44" id="vW7-2c-XUA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Continue">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="continueButtonAction:" destination="3Nb-ba-XcY" eventType="touchUpInside" id="56l-uP-Q22"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="d3k-sH-pkd" firstAttribute="top" secondItem="i5q-8U-NpS" secondAttribute="bottom" constant="20" id="0zY-vK-dcB"/>
|
||||
<constraint firstItem="0mL-Yj-ueq" firstAttribute="width" secondItem="d3k-sH-pkd" secondAttribute="width" priority="250" id="6aV-C7-iNC"/>
|
||||
<constraint firstItem="iUc-92-5Iv" firstAttribute="top" secondItem="4jF-pR-0xn" secondAttribute="bottom" constant="18" id="96I-eP-mad"/>
|
||||
<constraint firstItem="0mL-Yj-ueq" firstAttribute="top" secondItem="d3k-sH-pkd" secondAttribute="bottom" constant="20" id="9lC-9v-jhB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0mL-Yj-ueq" secondAttribute="bottom" constant="20" id="Ae2-ca-Wj8"/>
|
||||
<constraint firstItem="4jF-pR-0xn" firstAttribute="centerY" secondItem="F2c-As-8Ce" secondAttribute="centerY" id="EM7-qU-UJn"/>
|
||||
<constraint firstItem="i5q-8U-NpS" firstAttribute="trailing" secondItem="iUc-92-5Iv" secondAttribute="trailing" id="KFE-YJ-23G"/>
|
||||
<constraint firstAttribute="trailing" secondItem="d3k-sH-pkd" secondAttribute="trailing" constant="20" id="OVs-CR-hnR"/>
|
||||
<constraint firstItem="i5q-8U-NpS" firstAttribute="leading" secondItem="iUc-92-5Iv" secondAttribute="leading" id="Oid-gA-1Ul"/>
|
||||
<constraint firstItem="iUc-92-5Iv" firstAttribute="leading" secondItem="4jF-pR-0xn" secondAttribute="leading" id="cUc-9r-Z6J"/>
|
||||
<constraint firstItem="iUc-92-5Iv" firstAttribute="trailing" secondItem="F2c-As-8Ce" secondAttribute="trailing" id="e64-iy-oYE"/>
|
||||
<constraint firstItem="i5q-8U-NpS" firstAttribute="top" secondItem="iUc-92-5Iv" secondAttribute="bottom" constant="20" id="fEi-E8-VEb"/>
|
||||
<constraint firstItem="d3k-sH-pkd" firstAttribute="leading" secondItem="vsY-4j-uQz" secondAttribute="leading" constant="20" id="fXW-MO-s6w"/>
|
||||
<constraint firstItem="F2c-As-8Ce" firstAttribute="top" secondItem="vsY-4j-uQz" secondAttribute="top" constant="20" id="iKL-c9-I8u"/>
|
||||
<constraint firstItem="F2c-As-8Ce" firstAttribute="leading" secondItem="4jF-pR-0xn" secondAttribute="trailing" constant="10" id="io2-qE-Xmg"/>
|
||||
<constraint firstItem="0mL-Yj-ueq" firstAttribute="centerX" secondItem="vsY-4j-uQz" secondAttribute="centerX" id="tLS-tp-y26"/>
|
||||
<constraint firstItem="0mL-Yj-ueq" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vsY-4j-uQz" secondAttribute="leading" constant="20" id="tXE-bz-MoB"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="0mL-Yj-ueq" secondAttribute="trailing" constant="20" id="xHm-ec-RiN"/>
|
||||
<constraint firstAttribute="trailing" secondItem="F2c-As-8Ce" secondAttribute="trailing" constant="20" id="y9f-Kq-oYP"/>
|
||||
<constraint firstItem="4jF-pR-0xn" firstAttribute="leading" secondItem="vsY-4j-uQz" secondAttribute="leading" constant="20" id="yyc-kc-was"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vsY-4j-uQz" firstAttribute="top" secondItem="yPm-GN-iNf" secondAttribute="top" id="8Ca-HP-L6B"/>
|
||||
<constraint firstAttribute="bottom" secondItem="vsY-4j-uQz" secondAttribute="bottom" id="bEQ-7N-MIg"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vsY-4j-uQz" secondAttribute="trailing" id="dJ0-Fd-abb"/>
|
||||
<constraint firstItem="vsY-4j-uQz" firstAttribute="leading" secondItem="yPm-GN-iNf" secondAttribute="leading" id="eIi-U9-scV"/>
|
||||
<constraint firstItem="vsY-4j-uQz" firstAttribute="width" secondItem="yPm-GN-iNf" secondAttribute="width" id="zZN-Jx-lhS"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="yPm-GN-iNf" secondAttribute="bottom" id="AVK-pe-mSa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="yPm-GN-iNf" secondAttribute="trailing" id="Pm4-AI-Yah"/>
|
||||
<constraint firstItem="yPm-GN-iNf" firstAttribute="leading" secondItem="Z4L-Ka-Saj" secondAttribute="leading" id="Spn-UK-HMu"/>
|
||||
<constraint firstItem="yPm-GN-iNf" firstAttribute="top" secondItem="0Vv-Fe-78X" secondAttribute="top" id="qkB-1D-obj"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="0Vv-Fe-78X"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="closeButton" destination="F2c-As-8Ce" id="dJz-ii-CtQ"/>
|
||||
<outlet property="continueButton" destination="0mL-Yj-ueq" id="TlH-WR-YZc"/>
|
||||
<outlet property="creatorAvatarImageView" destination="UvZ-W0-l9O" id="GcL-Cf-bKe"/>
|
||||
<outlet property="creatorDisplayNameLabel" destination="FjU-fb-8u1" id="CnD-hH-yax"/>
|
||||
<outlet property="creatorInfoTitleLabel" destination="iUc-92-5Iv" id="YB7-M7-v5p"/>
|
||||
<outlet property="creatorUserIDLabel" destination="Y8T-5r-qPI" id="gB0-wH-0Vg"/>
|
||||
<outlet property="informationLabel" destination="d3k-sH-pkd" id="LYA-Fi-nkb"/>
|
||||
<outlet property="scrollView" destination="yPm-GN-iNf" id="c9d-T1-4hN"/>
|
||||
<outlet property="titleLabel" destination="4jF-pR-0xn" id="6UD-Nz-fic"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="WZQ-Wb-Bbh" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-175" y="125"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="close_button" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,229 @@
|
|||
// 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
|
||||
|
||||
@objc
|
||||
final class WidgetPermissionViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let continueButtonCornerRadius: CGFloat = 8.0
|
||||
}
|
||||
|
||||
private enum Sizing {
|
||||
static var viewController: WidgetPermissionViewController?
|
||||
static var widthConstraint: NSLayoutConstraint?
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
|
||||
@IBOutlet private weak var creatorInfoTitleLabel: UILabel!
|
||||
@IBOutlet private weak var creatorAvatarImageView: MXKImageView!
|
||||
@IBOutlet private weak var creatorDisplayNameLabel: UILabel!
|
||||
@IBOutlet private weak var creatorUserIDLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var continueButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: WidgetPermissionViewModel! {
|
||||
didSet {
|
||||
self.updateViews()
|
||||
}
|
||||
}
|
||||
private var theme: Theme!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@objc var didTapCloseButton: (() -> Void)?
|
||||
@objc var didTapContinueButton: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@objc class func instantiate(with viewModel: WidgetPermissionViewModel) -> WidgetPermissionViewController {
|
||||
let viewController = StoryboardScene.WidgetPermissionViewController.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.updateViews()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.scrollView.flashScrollIndicators()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.creatorAvatarImageView.layer.cornerRadius = self.creatorAvatarImageView.frame.size.width/2
|
||||
self.continueButton.layer.cornerRadius = Constants.continueButtonCornerRadius
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.frame.size.width/2
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.closeButton.vc_setBackgroundColor(theme.headerTextSecondaryColor, for: .normal)
|
||||
|
||||
self.creatorInfoTitleLabel.textColor = theme.textSecondaryColor
|
||||
self.creatorDisplayNameLabel.textColor = theme.textSecondaryColor
|
||||
self.creatorUserIDLabel.textColor = theme.textSecondaryColor
|
||||
|
||||
self.informationLabel.textColor = theme.textSecondaryColor
|
||||
|
||||
self.continueButton.vc_setBackgroundColor(theme.tintColor, 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.closeButton.layer.masksToBounds = true
|
||||
|
||||
self.setupCreatorAvatarImageView()
|
||||
|
||||
self.titleLabel.text = VectorL10n.roomWidgetPermissionTitle
|
||||
self.creatorInfoTitleLabel.text = VectorL10n.roomWidgetPermissionCreatorInfoTitle
|
||||
self.informationLabel.text = ""
|
||||
|
||||
self.setupContinueButton()
|
||||
}
|
||||
|
||||
private func updateViews() {
|
||||
|
||||
if let avatarImageView = self.creatorAvatarImageView {
|
||||
let defaultavatarImage = AvatarGenerator.generateAvatar(forMatrixItem: self.viewModel.creatorUserId, withDisplayName: self.viewModel.creatorDisplayName)
|
||||
avatarImageView.setImageURI(self.viewModel.creatorAvatarUrl, withType: nil, andImageOrientation: .up, previewImage: defaultavatarImage, mediaManager: self.viewModel.mediaManager)
|
||||
}
|
||||
|
||||
if let creatorDisplayNameLabel = self.creatorDisplayNameLabel {
|
||||
if let creatorDisplayName = self.viewModel.creatorDisplayName {
|
||||
creatorDisplayNameLabel.text = creatorDisplayName
|
||||
} else {
|
||||
creatorDisplayNameLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
if let creatorUserIDLabel = self.creatorUserIDLabel {
|
||||
creatorUserIDLabel.text = self.viewModel.creatorUserId
|
||||
}
|
||||
|
||||
if let informationLabel = self.informationLabel {
|
||||
informationLabel.text = self.viewModel.permissionsInformationText
|
||||
}
|
||||
}
|
||||
|
||||
private func setupCreatorAvatarImageView() {
|
||||
self.creatorAvatarImageView.defaultBackgroundColor = UIColor.clear
|
||||
self.creatorAvatarImageView.enableInMemoryCache = true
|
||||
self.creatorAvatarImageView.clipsToBounds = true
|
||||
}
|
||||
|
||||
private func setupContinueButton() {
|
||||
self.continueButton.layer.masksToBounds = true
|
||||
self.continueButton.setTitle(VectorL10n.continue, for: .normal)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func closeButtonAction(_ sender: Any) {
|
||||
self.didTapCloseButton?()
|
||||
}
|
||||
|
||||
@IBAction private func continueButtonAction(_ sender: Any) {
|
||||
self.didTapContinueButton?()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SlidingModalPresentable
|
||||
extension WidgetPermissionViewController: SlidingModalPresentable {
|
||||
|
||||
func allowsDismissOnBackgroundTap() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
|
||||
|
||||
let sizingViewContoller: WidgetPermissionViewController
|
||||
|
||||
if let viewController = WidgetPermissionViewController.Sizing.viewController {
|
||||
viewController.viewModel = self.viewModel
|
||||
sizingViewContoller = viewController
|
||||
} else {
|
||||
sizingViewContoller = WidgetPermissionViewController.instantiate(with: self.viewModel)
|
||||
WidgetPermissionViewController.Sizing.viewController = sizingViewContoller
|
||||
}
|
||||
|
||||
let sizingViewContollerView: UIView = sizingViewContoller.view
|
||||
|
||||
if let widthConstraint = WidgetPermissionViewController.Sizing.widthConstraint {
|
||||
widthConstraint.constant = width
|
||||
} else {
|
||||
let widthConstraint = sizingViewContollerView.widthAnchor.constraint(equalToConstant: width)
|
||||
widthConstraint.isActive = true
|
||||
WidgetPermissionViewController.Sizing.widthConstraint = widthConstraint
|
||||
|
||||
sizingViewContollerView.heightAnchor.constraint(equalToConstant: 0)
|
||||
}
|
||||
|
||||
sizingViewContollerView.layoutIfNeeded()
|
||||
|
||||
return sizingViewContoller.scrollView.contentSize.height
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// View model used by `WidgetPermissionViewController`
|
||||
@objcMembers
|
||||
final class WidgetPermissionViewModel: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let creatorUserId: String
|
||||
let creatorDisplayName: String?
|
||||
let creatorAvatarUrl: String?
|
||||
let widgetDomain: String?
|
||||
let isWebviewWidget: Bool
|
||||
let widgetPermissions: [String]
|
||||
let mediaManager: MXMediaManager
|
||||
|
||||
lazy var permissionsInformationText: String = {
|
||||
return self.buildPermissionsInformationText()
|
||||
}()
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(creatorUserId: String, creatorDisplayName: String?, creatorAvatarUrl: String?, widgetDomain: String?, isWebviewWidget: Bool, widgetPermissions: [String], mediaManager: MXMediaManager) {
|
||||
self.creatorUserId = creatorUserId
|
||||
self.creatorDisplayName = creatorDisplayName
|
||||
self.creatorAvatarUrl = creatorAvatarUrl
|
||||
self.widgetDomain = widgetDomain
|
||||
self.isWebviewWidget = isWebviewWidget
|
||||
self.widgetPermissions = widgetPermissions
|
||||
self.mediaManager = mediaManager
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildPermissionsInformationText() -> String {
|
||||
|
||||
let informationTitle: String
|
||||
let widgetDomain = self.widgetDomain ?? ""
|
||||
|
||||
if self.isWebviewWidget {
|
||||
informationTitle = VectorL10n.roomWidgetPermissionWebviewInformationTitle(widgetDomain)
|
||||
} else {
|
||||
informationTitle = VectorL10n.roomWidgetPermissionInformationTitle(widgetDomain)
|
||||
}
|
||||
|
||||
let permissionsList = self.widgetPermissions.reduce("") { (accumulatedPermissions, permission) -> String in
|
||||
return accumulatedPermissions + "\n• \(permission)"
|
||||
}
|
||||
|
||||
return informationTitle + permissionsList
|
||||
}
|
||||
}
|
|
@ -21,13 +21,19 @@
|
|||
#import "WidgetManager.h"
|
||||
#import "WidgetViewController.h"
|
||||
#import "IntegrationManagerViewController.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface WidgetPickerViewController ()
|
||||
@interface WidgetPickerViewController () <ServiceTermsModalCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
MXSession *mxSession;
|
||||
NSString *roomId;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) UIViewController *presentingViewController;
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) MXKRoomDataSource *roomDataSource;
|
||||
@property (nonatomic, strong) Widget *selectedWidget;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WidgetPickerViewController
|
||||
|
@ -67,27 +73,14 @@
|
|||
{
|
||||
// Hide back button title
|
||||
mxkViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
||||
|
||||
// Display the widget
|
||||
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
|
||||
|
||||
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
|
||||
|
||||
widgetVC.roomDataSource = roomDataSource;
|
||||
|
||||
[mxkViewController.navigationController pushViewController:widgetVC animated:YES];
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
NSLog(@"[WidgetPickerVC] Cannot display widget %@", widget);
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
|
||||
[self fetchWidgetURLAndDisplayUsingWidget:widget canPresentServiceTerms:YES];
|
||||
}];
|
||||
[self.alertController addAction:alertAction];
|
||||
}
|
||||
|
||||
// Link to the integration manager
|
||||
alertAction = [UIAlertAction actionWithTitle:@"Manage integrations..."
|
||||
alertAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"widget_picker_manage_integrations", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action)
|
||||
{
|
||||
|
@ -107,8 +100,88 @@
|
|||
[self.alertController addAction:alertAction];
|
||||
|
||||
// And show it
|
||||
[mxkViewController presentViewController:_alertController animated:YES completion:nil];
|
||||
[mxkViewController presentViewController:self.alertController animated:YES completion:nil];
|
||||
|
||||
self.presentingViewController = mxkViewController;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchWidgetURLAndDisplayUsingWidget:(Widget*)widget canPresentServiceTerms:(BOOL)canPresentServiceTerms
|
||||
{
|
||||
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
|
||||
|
||||
// Display the widget
|
||||
|
||||
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
|
||||
|
||||
widgetVC.roomDataSource = self.roomDataSource;
|
||||
|
||||
[self.presentingViewController.navigationController pushViewController:widgetVC animated:YES];
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
NSLog(@"[WidgetPickerVC] Get widget URL failed with error: %@", error);
|
||||
|
||||
if (canPresentServiceTerms
|
||||
&& [error.domain isEqualToString:WidgetManagerErrorDomain]
|
||||
&& error.code == WidgetManagerErrorCodeTermsNotSigned)
|
||||
{
|
||||
[self presentTermsForWidget:widget];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Service terms
|
||||
|
||||
- (void)presentTermsForWidget:(Widget*)widget
|
||||
{
|
||||
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.presentingViewController animated:YES];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
|
||||
{
|
||||
MXWeakify(self);
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (self.selectedWidget)
|
||||
{
|
||||
[self fetchWidgetURLAndDisplayUsingWidget:self.selectedWidget canPresentServiceTerms:NO];
|
||||
}
|
||||
}];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter session:(MXSession * _Nonnull)session
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -30,7 +30,11 @@ final class JitsiService: NSObject {
|
|||
JMCallKitProxy.enabled = enableCallKit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var serverURL: URL? {
|
||||
return self.jitsiMeet.defaultConferenceOptions?.serverURL
|
||||
}
|
||||
|
||||
private let jitsiMeet = JitsiMeet.sharedInstance()
|
||||
|
||||
// MARK: - Setup
|
||||
|
|
|
@ -146,12 +146,26 @@ static const NSString *kJitsiDataErrorKey = @"error";
|
|||
{
|
||||
if (conferenceId)
|
||||
{
|
||||
// TODO: Set up user info but it is not yet available in the jitsi-meet iOS SDK
|
||||
// See https://github.com/jitsi/jitsi-meet/issues/1880
|
||||
|
||||
JitsiMeetConferenceOptions *jitsiMeetConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder * _Nonnull jitsiMeetConferenceOptionsBuilder) {
|
||||
jitsiMeetConferenceOptionsBuilder.room = conferenceId;
|
||||
jitsiMeetConferenceOptionsBuilder.videoMuted = !self.startWithVideo;
|
||||
// Get info about the room and our user
|
||||
MXSession *session = self.widget.mxSession;
|
||||
MXRoomSummary *roomSummary = [session roomSummaryWithRoomId:self.widget.roomId];
|
||||
|
||||
MXRoom *room = [session roomWithRoomId:self.widget.roomId];
|
||||
MXRoomMember *roomMember = [room.dangerousSyncState.members memberWithUserId:session.myUser.userId];
|
||||
|
||||
NSString *userDisplayName = roomMember.displayname;
|
||||
NSString *avatar = [session.mediaManager urlOfContent:roomMember.avatarUrl];
|
||||
NSURL *avatarUrl = [NSURL URLWithString:avatar];
|
||||
|
||||
JitsiMeetConferenceOptions *jitsiMeetConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder * _Nonnull builder) {
|
||||
|
||||
builder.room = conferenceId;
|
||||
builder.videoMuted = !self.startWithVideo;
|
||||
|
||||
builder.subject = roomSummary.displayname;
|
||||
builder.userInfo = [[JitsiMeetUserInfo alloc] initWithDisplayName:userDisplayName
|
||||
andEmail:nil
|
||||
andAvatar:avatarUrl];
|
||||
}];
|
||||
|
||||
[self.jitsiMeetView join:jitsiMeetConferenceOptions];
|
||||
|
|
|
@ -26,6 +26,9 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
|
|||
@interface WidgetViewController () <ServiceTermsModalCoordinatorBridgePresenterDelegate>
|
||||
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) NSString *widgetUrl;
|
||||
|
||||
@property (nonatomic, strong) SlidingModalPresenter *slidingModalPresenter;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -34,9 +37,12 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
|
|||
|
||||
- (instancetype)initWithUrl:(NSString*)widgetUrl forWidget:(Widget*)theWidget
|
||||
{
|
||||
self = [super initWithURL:widgetUrl];
|
||||
// The opening of the url is delayed in viewWillAppear where we will check
|
||||
// the widget permission
|
||||
self = [super initWithURL:nil];
|
||||
if (self)
|
||||
{
|
||||
self.widgetUrl = widgetUrl;
|
||||
widget = theWidget;
|
||||
}
|
||||
return self;
|
||||
|
@ -54,6 +60,74 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
|
|||
if (widget)
|
||||
{
|
||||
self.navigationItem.title = widget.name ? widget.name : widget.type;
|
||||
|
||||
UIBarButtonItem *menuButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"room_context_menu_more"] style:UIBarButtonItemStylePlain target:self action:@selector(onMenuButtonPressed:)];
|
||||
self.navigationItem.rightBarButtonItem = menuButton;
|
||||
}
|
||||
|
||||
self.slidingModalPresenter = [SlidingModalPresenter new];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Check widget permission before opening the widget
|
||||
[self checkWidgetPermissionWithCompletion:^(BOOL granted) {
|
||||
|
||||
[self.slidingModalPresenter dismissWithAnimated:YES completion:nil];
|
||||
|
||||
if (granted)
|
||||
{
|
||||
self.URL = self.widgetUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reloadWidget
|
||||
{
|
||||
self.URL = self.widgetUrl;
|
||||
}
|
||||
|
||||
- (BOOL)hasUserEnoughPowerToManageCurrentWidget
|
||||
{
|
||||
BOOL hasUserEnoughPower = NO;
|
||||
|
||||
MXSession *session = widget.mxSession;
|
||||
MXRoom *room = [session roomWithRoomId:self.widget.roomId];
|
||||
MXRoomState *roomState = room.dangerousSyncState;
|
||||
if (roomState)
|
||||
{
|
||||
// Check user's power in the room
|
||||
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
|
||||
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:session.myUser.userId];
|
||||
|
||||
// The user must be able to send state events to manage widgets
|
||||
if (oneSelfPowerLevel >= powerLevels.stateDefault)
|
||||
{
|
||||
hasUserEnoughPower = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return hasUserEnoughPower;
|
||||
}
|
||||
|
||||
- (void)removeCurrentWidget
|
||||
{
|
||||
WidgetManager *widgetManager = [WidgetManager sharedManager];
|
||||
|
||||
MXRoom *room = [self.widget.mxSession roomWithRoomId:self.widget.roomId];
|
||||
NSString *widgetId = self.widget.widgetId;
|
||||
if (room && widgetId)
|
||||
{
|
||||
[widgetManager closeWidget:widgetId inRoom:room success:^{
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[WidgetVC] removeCurrentWidget failed. Error: %@", error);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +168,182 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
|
|||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Widget Permission
|
||||
|
||||
- (void)checkWidgetPermissionWithCompletion:(void (^)(BOOL granted))completion
|
||||
{
|
||||
MXSession *session = widget.mxSession;
|
||||
|
||||
if ([widget.widgetEvent.sender isEqualToString:session.myUser.userId])
|
||||
{
|
||||
// No need of more permission check if the user created the widget
|
||||
completion(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permission in user Riot settings
|
||||
__block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
|
||||
WidgetPermission permission = [sharedSettings permissionFor:widget];
|
||||
if (permission == WidgetPermissionGranted)
|
||||
{
|
||||
completion(YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: ask permission again if the user previously declined it
|
||||
[self askPermissionWithCompletion:^(BOOL granted) {
|
||||
// Update the settings in user account data in parallel
|
||||
[sharedSettings setPermission:granted ? WidgetPermissionGranted : WidgetPermissionDeclined
|
||||
for:self.widget
|
||||
success:^
|
||||
{
|
||||
sharedSettings = nil;
|
||||
}
|
||||
failure:^(NSError * _Nullable error)
|
||||
{
|
||||
NSLog(@"[WidgetVC] setPermissionForWidget failed. Error: %@", error);
|
||||
sharedSettings = nil;
|
||||
}];
|
||||
|
||||
completion(granted);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)askPermissionWithCompletion:(void (^)(BOOL granted))completion
|
||||
{
|
||||
NSString *widgetCreatorUserId = self.widget.widgetEvent.sender ?: NSLocalizedStringFromTable(@"room_participants_unknown", @"Vector", nil);
|
||||
|
||||
MXSession *session = widget.mxSession;
|
||||
MXRoom *room = [session roomWithRoomId:self.widget.widgetEvent.roomId];
|
||||
MXRoomState *roomState = room.dangerousSyncState;
|
||||
MXRoomMember *widgetCreatorRoomMember = [roomState.members memberWithUserId:widgetCreatorUserId];
|
||||
|
||||
NSString *widgetDomain = @"";
|
||||
|
||||
if (widget.url)
|
||||
{
|
||||
NSString *host = [[NSURL alloc] initWithString:widget.url].host;
|
||||
if (host)
|
||||
{
|
||||
widgetDomain = host;
|
||||
}
|
||||
}
|
||||
|
||||
MXMediaManager *mediaManager = widget.mxSession.mediaManager;
|
||||
NSString *widgetCreatorDisplayName = widgetCreatorRoomMember.displayname;
|
||||
NSString *widgetCreatorAvatarURL = widgetCreatorRoomMember.avatarUrl;
|
||||
|
||||
NSArray<NSString*> *permissionStrings = @[
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_display_name_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_avatar_url_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_user_id_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_theme_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_widget_id_permission", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"room_widget_permission_room_id_permission", @"Vector", nil)
|
||||
];
|
||||
|
||||
|
||||
WidgetPermissionViewModel *widgetPermissionViewModel = [[WidgetPermissionViewModel alloc] initWithCreatorUserId:widgetCreatorUserId
|
||||
creatorDisplayName:widgetCreatorDisplayName creatorAvatarUrl:widgetCreatorAvatarURL widgetDomain:widgetDomain
|
||||
isWebviewWidget:YES
|
||||
widgetPermissions:permissionStrings
|
||||
mediaManager:mediaManager];
|
||||
|
||||
|
||||
WidgetPermissionViewController *widgetPermissionViewController = [WidgetPermissionViewController instantiateWith:widgetPermissionViewModel];
|
||||
|
||||
widgetPermissionViewController.didTapContinueButton = ^{
|
||||
completion(YES);
|
||||
};
|
||||
|
||||
widgetPermissionViewController.didTapCloseButton = ^{
|
||||
completion(NO);
|
||||
};
|
||||
|
||||
|
||||
[self.slidingModalPresenter present:widgetPermissionViewController from:self animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)revokePermissionForCurrentWidget
|
||||
{
|
||||
MXSession *session = widget.mxSession;
|
||||
__block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
|
||||
[sharedSettings setPermission:WidgetPermissionDeclined for:widget success:^{
|
||||
sharedSettings = nil;
|
||||
} failure:^(NSError * _Nullable error) {
|
||||
NSLog(@"[WidgetVC] revokePermissionForCurrentWidget failed. Error: %@", error);
|
||||
sharedSettings = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Contextual Menu
|
||||
|
||||
- (IBAction)onMenuButtonPressed:(id)sender
|
||||
{
|
||||
[self showMenu];
|
||||
}
|
||||
|
||||
-(void)showMenu
|
||||
{
|
||||
MXSession *session = widget.mxSession;
|
||||
|
||||
UIAlertController *menu = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
[menu addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"widget_menu_refresh", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[self reloadWidget];
|
||||
}]];
|
||||
|
||||
NSURL *url = [NSURL URLWithString:self.widgetUrl];
|
||||
if (url && [[UIApplication sharedApplication] canOpenURL:url])
|
||||
{
|
||||
[menu addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"widget_menu_open_outside", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
|
||||
}];
|
||||
}]];
|
||||
}
|
||||
|
||||
if (![widget.widgetEvent.sender isEqualToString:session.myUser.userId])
|
||||
{
|
||||
[menu addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"widget_menu_revoke_permission", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[self revokePermissionForCurrentWidget];
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}]];
|
||||
}
|
||||
|
||||
if ([self hasUserEnoughPowerToManageCurrentWidget])
|
||||
{
|
||||
[menu addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"widget_menu_remove", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[self removeCurrentWidget];
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}]];
|
||||
}
|
||||
|
||||
[menu addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
}]];
|
||||
|
||||
[self presentViewController:menu animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - WKNavigationDelegate
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
|
||||
|
@ -435,4 +685,12 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse
|
|||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter session:(MXSession * _Nonnull)session
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}];
|
||||
self.serviceTermsModalCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -228,6 +228,7 @@
|
|||
@property (nonatomic, strong) ReactionHistoryCoordinatorBridgePresenter *reactionHistoryCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) CameraPresenter *cameraPresenter;
|
||||
@property (nonatomic, strong) MediaPickerCoordinatorBridgePresenter *mediaPickerPresenter;
|
||||
@property (nonatomic, strong) RoomMessageURLParser *roomMessageURLParser;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -423,6 +424,7 @@
|
|||
|
||||
self.roomContextualMenuPresenter = [RoomContextualMenuPresenter new];
|
||||
self.errorPresenter = [MXKErrorAlertPresentation new];
|
||||
self.roomMessageURLParser = [RoomMessageURLParser new];
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
@ -2915,6 +2917,13 @@
|
|||
// Retrieve the type of interaction expected with the URL (See UITextItemInteraction)
|
||||
NSNumber *urlItemInteractionValue = userInfo[kMXKRoomBubbleCellUrlItemInteraction];
|
||||
|
||||
RoomMessageURLType roomMessageURLType = RoomMessageURLTypeUnknown;
|
||||
|
||||
if (url)
|
||||
{
|
||||
roomMessageURLType = [self.roomMessageURLParser parseURL:url];
|
||||
}
|
||||
|
||||
// When a link refers to a room alias/id, a user id or an event id, the non-ASCII characters (like '#' in room alias) has been escaped
|
||||
// to be able to convert it into a legal URL string.
|
||||
NSString *absoluteURLString = [url.absoluteString stringByRemovingPercentEncoding];
|
||||
|
@ -3007,19 +3016,40 @@
|
|||
// Fallback case for external links
|
||||
switch (urlItemInteractionValue.integerValue) {
|
||||
case UITextItemInteractionInvokeDefaultAction:
|
||||
{
|
||||
[[UIApplication sharedApplication] vc_open:url completionHandler:^(BOOL success) {
|
||||
if (!success)
|
||||
{
|
||||
[self showUnableToOpenLinkErrorAlert];
|
||||
}
|
||||
}];
|
||||
shouldDoAction = NO;
|
||||
{
|
||||
switch (roomMessageURLType) {
|
||||
case RoomMessageURLTypeAppleDataDetector:
|
||||
// Keep the default OS behavior on single tap when UITextView data detector detect a known type.
|
||||
shouldDoAction = YES;
|
||||
break;
|
||||
case RoomMessageURLTypeDummy:
|
||||
// Do nothing for dummy links
|
||||
shouldDoAction = NO;
|
||||
break;
|
||||
default:
|
||||
// Try to open the link
|
||||
[[UIApplication sharedApplication] vc_open:url completionHandler:^(BOOL success) {
|
||||
if (!success)
|
||||
{
|
||||
[self showUnableToOpenLinkErrorAlert];
|
||||
}
|
||||
}];
|
||||
shouldDoAction = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UITextItemInteractionPresentActions:
|
||||
{
|
||||
// Long press on link, present room contextual menu.
|
||||
// Retrieve the tapped event
|
||||
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
|
||||
|
||||
if (tappedEvent)
|
||||
{
|
||||
// Long press on link, present room contextual menu.
|
||||
[self showContextualMenuForEvent:tappedEvent fromSingleTapGesture:NO cell:cell animated:YES];
|
||||
}
|
||||
|
||||
shouldDoAction = NO;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -224,11 +224,23 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
|||
guard let policyIndex = sender.view?.tag else {
|
||||
return
|
||||
}
|
||||
|
||||
let isCheckBoxSelected: Bool
|
||||
|
||||
if self.checkedPolicies.contains(policyIndex) {
|
||||
self.checkedPolicies.remove(policyIndex)
|
||||
isCheckBoxSelected = false
|
||||
} else {
|
||||
checkedPolicies.insert(policyIndex)
|
||||
isCheckBoxSelected = true
|
||||
}
|
||||
|
||||
if let checkBoxImageView = sender.view as? UIImageView {
|
||||
if isCheckBoxSelected {
|
||||
checkBoxImageView.accessibilityTraits.insert(.selected)
|
||||
} else {
|
||||
checkBoxImageView.accessibilityTraits.remove(.selected)
|
||||
}
|
||||
}
|
||||
|
||||
self.refreshViews()
|
||||
|
@ -275,6 +287,11 @@ extension ServiceTermsModalScreenViewController: UITableViewDataSource {
|
|||
checkBox.isUserInteractionEnabled = true
|
||||
checkBox.tag = indexPath.row
|
||||
checkBox.addGestureRecognizer(gesture)
|
||||
|
||||
checkBox.isAccessibilityElement = true
|
||||
checkBox.accessibilityTraits = .button
|
||||
checkBox.accessibilityLabel = VectorL10n.accessibilityCheckboxLabel
|
||||
checkBox.accessibilityHint = VectorL10n.serviceTermsModalPolicyCheckboxAccessibilityHint(policy.name)
|
||||
}
|
||||
|
||||
return cell
|
||||
|
|
|
@ -57,6 +57,7 @@ enum
|
|||
SETTINGS_SECTION_IDENTITY_SERVER_INDEX,
|
||||
SETTINGS_SECTION_CONTACTS_INDEX,
|
||||
SETTINGS_SECTION_IGNORED_USERS_INDEX,
|
||||
SETTINGS_SECTION_INTEGRATIONS_INDEX,
|
||||
SETTINGS_SECTION_USER_INTERFACE_INDEX,
|
||||
SETTINGS_SECTION_ADVANCED_INDEX,
|
||||
SETTINGS_SECTION_OTHER_INDEX,
|
||||
|
@ -94,6 +95,13 @@ enum
|
|||
CALLS_COUNT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
INTEGRATIONS_INDEX,
|
||||
INTEGRATIONS_DESCRIPTION_INDEX,
|
||||
INTEGRATIONS_COUNT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
USER_INTERFACE_LANGUAGE_INDEX = 0,
|
||||
|
@ -129,7 +137,10 @@ enum
|
|||
LABS_USE_ROOM_MEMBERS_LAZY_LOADING_INDEX = 0,
|
||||
LABS_USE_JITSI_WIDGET_INDEX,
|
||||
LABS_CRYPTO_INDEX,
|
||||
LABS_COUNT
|
||||
LABS_COUNT, // TODO: Remove it once features exist
|
||||
LABS_DM_KEY_VERIFICATION_INDEX,
|
||||
LABS_CROSS_SIGNING_INDEX,
|
||||
// LABS_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1390,6 +1401,10 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
{
|
||||
count = IDENTITY_SERVER_COUNT;
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_INTEGRATIONS_INDEX)
|
||||
{
|
||||
count = INTEGRATIONS_COUNT;
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
|
||||
{
|
||||
count = USER_INTERFACE_COUNT;
|
||||
|
@ -2057,6 +2072,44 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_INTEGRATIONS_INDEX)
|
||||
{
|
||||
switch (row) {
|
||||
case INTEGRATIONS_INDEX:
|
||||
{
|
||||
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_integrations_allow_button", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = sharedSettings.hasIntegrationProvisioningEnabled;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
labelAndSwitchCell.mxkSwitch.enabled = YES;
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleAllowIntegrations:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
break;
|
||||
}
|
||||
|
||||
case INTEGRATIONS_DESCRIPTION_INDEX:
|
||||
{
|
||||
MXKTableViewCell *descriptionCell = [self getDefaultTableViewCell:tableView];
|
||||
|
||||
NSString *integrationManager = [WidgetManager.sharedManager configForUser:session.myUser.userId].apiUrl;
|
||||
NSString *integrationManagerDomain = [NSURL URLWithString:integrationManager].host;
|
||||
|
||||
NSString *description = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_integrations_allow_description", @"Vector", nil), integrationManagerDomain];
|
||||
descriptionCell.textLabel.text = description;
|
||||
descriptionCell.textLabel.numberOfLines = 0;
|
||||
descriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
cell = descriptionCell;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
|
||||
{
|
||||
if (row == USER_INTERFACE_LANGUAGE_INDEX)
|
||||
|
@ -2395,6 +2448,30 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
labelAndSwitchCell.mxkSwitch.enabled = NO;
|
||||
}
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == LABS_DM_KEY_VERIFICATION_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_dm_key_verification", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableDMKeyVerification;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsDMKeyVerification:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else if (row == LABS_CROSS_SIGNING_INDEX)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_cross_signing", @"Vector", nil);
|
||||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableCrossSigning;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsCrossSigning:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
}
|
||||
|
@ -2564,6 +2641,10 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
{
|
||||
return NSLocalizedStringFromTable(@"settings_identity_server_settings", @"Vector", nil);
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_INTEGRATIONS_INDEX)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"settings_integrations", @"Vector", nil);
|
||||
}
|
||||
else if (section == SETTINGS_SECTION_USER_INTERFACE_INDEX)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"settings_user_interface", @"Vector", nil);
|
||||
|
@ -3187,6 +3268,24 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? RiotSettings.shared.stunServerFallback : nil;
|
||||
}
|
||||
|
||||
- (void)toggleAllowIntegrations:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
MXSession *session = self.mainSession;
|
||||
[self startActivityIndicator];
|
||||
|
||||
__block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
|
||||
[sharedSettings setIntegrationProvisioningWithEnabled:switchButton.on success:^{
|
||||
sharedSettings = nil;
|
||||
[self stopActivityIndicator];
|
||||
} failure:^(NSError * _Nullable error) {
|
||||
sharedSettings = nil;
|
||||
[switchButton setOn:!switchButton.on animated:YES];
|
||||
[self stopActivityIndicator];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)toggleShowDecodedContent:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
@ -3425,6 +3524,20 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleLabsDMKeyVerification:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
RiotSettings.shared.enableDMKeyVerification = switchButton.isOn;
|
||||
}
|
||||
|
||||
- (void)toggleLabsCrossSigning:(id)sender
|
||||
{
|
||||
UISwitch *switchButton = (UISwitch*)sender;
|
||||
|
||||
RiotSettings.shared.enableCrossSigning = switchButton.isOn;
|
||||
}
|
||||
|
||||
- (void)toggleBlacklistUnverifiedDevices:(id)sender
|
||||
{
|
||||
|
|
165
Riot/Modules/SlidingModal/SlidingModalContainerView.swift
Normal file
165
Riot/Modules/SlidingModal/SlidingModalContainerView.swift
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
protocol SlidingModalContainerViewDelegate: class {
|
||||
func slidingModalContainerViewDidTapBackground(_ view: SlidingModalContainerView)
|
||||
}
|
||||
|
||||
/// `SlidingModalContainerView` is a custom UIView used as a `UIViewControllerContextTransitioning.containerView` subview to embed a `SlidingModalPresentable` during presentation.
|
||||
final class SlidingModalContainerView: UIView, Themable, NibLoadable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let cornerRadius: CGFloat = 12.0
|
||||
static let dimmingColorAlpha: CGFloat = 0.7
|
||||
}
|
||||
|
||||
private enum Sizing {
|
||||
static let view = SlidingModalContainerView.loadFromNib()
|
||||
static var widthConstraint: NSLayoutConstraint?
|
||||
static var heightConstraint: NSLayoutConstraint?
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var dimmingView: UIView!
|
||||
@IBOutlet private weak var contentView: UIView!
|
||||
|
||||
@IBOutlet private weak var contentViewHeightConstraint: NSLayoutConstraint!
|
||||
@IBOutlet private weak var contentViewBottomConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var dismissContentViewBottomConstant: CGFloat {
|
||||
let bottomSafeAreaHeight: CGFloat
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeAreaHeight = self.contentView.safeAreaInsets.bottom
|
||||
} else {
|
||||
bottomSafeAreaHeight = 0
|
||||
}
|
||||
|
||||
return -(self.contentViewHeightConstraint.constant + bottomSafeAreaHeight)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var contentViewFrame: CGRect {
|
||||
return self.contentView.frame
|
||||
}
|
||||
|
||||
weak var delegate: SlidingModalContainerViewDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate() -> SlidingModalContainerView {
|
||||
return SlidingModalContainerView.loadFromNib()
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.contentView.layer.masksToBounds = true
|
||||
self.dimmingView.backgroundColor = UIColor.black.withAlphaComponent(Constants.dimmingColorAlpha)
|
||||
|
||||
self.setupBackgroundTapGestureRecognizer()
|
||||
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.contentView.layer.cornerRadius = Constants.cornerRadius
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func preparePresentAnimation() {
|
||||
self.contentViewBottomConstraint.constant = 0
|
||||
}
|
||||
|
||||
func prepareDismissAnimation() {
|
||||
self.contentViewBottomConstraint.constant = self.dismissContentViewBottomConstant
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.contentView.backgroundColor = theme.headerBackgroundColor
|
||||
}
|
||||
|
||||
func updateContentViewMaxHeight(_ maxHeight: CGFloat) {
|
||||
self.contentViewHeightConstraint.constant = maxHeight
|
||||
}
|
||||
|
||||
func updateContentViewLayout() {
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
func setContentView(_ contentView: UIView) {
|
||||
for subView in self.contentView.subviews {
|
||||
subView.removeFromSuperview()
|
||||
}
|
||||
self.contentView.vc_addSubViewMatchingParent(contentView)
|
||||
}
|
||||
|
||||
func updateDimmingViewAlpha(_ alpha: CGFloat) {
|
||||
self.dimmingView.alpha = alpha
|
||||
}
|
||||
|
||||
func contentViewWidthFittingSize(_ size: CGSize) -> CGFloat {
|
||||
let sizingView = SlidingModalContainerView.Sizing.view
|
||||
|
||||
if let widthConstraint = SlidingModalContainerView.Sizing.widthConstraint {
|
||||
widthConstraint.constant = size.width
|
||||
} else {
|
||||
let widthConstraint = sizingView.widthAnchor.constraint(equalToConstant: size.width)
|
||||
widthConstraint.isActive = true
|
||||
SlidingModalContainerView.Sizing.widthConstraint = widthConstraint
|
||||
}
|
||||
|
||||
if let heightConstraint = SlidingModalContainerView.Sizing.heightConstraint {
|
||||
heightConstraint.constant = size.height
|
||||
} else {
|
||||
let heightConstraint = sizingView.heightAnchor.constraint(equalToConstant: size.width)
|
||||
heightConstraint.isActive = true
|
||||
SlidingModalContainerView.Sizing.heightConstraint = heightConstraint
|
||||
}
|
||||
|
||||
sizingView.setNeedsLayout()
|
||||
sizingView.layoutIfNeeded()
|
||||
|
||||
return sizingView.contentViewFrame.width
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupBackgroundTapGestureRecognizer() {
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundTap(_:)))
|
||||
self.dimmingView.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc private func handleBackgroundTap(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
self.delegate?.slidingModalContainerViewDidTapBackground(self)
|
||||
}
|
||||
}
|
54
Riot/Modules/SlidingModal/SlidingModalContainerView.xib
Normal file
54
Riot/Modules/SlidingModal/SlidingModalContainerView.xib
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<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>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SlidingModalContainerView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view alpha="0.69999999999999996" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="53q-mu-x6X">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<color key="backgroundColor" white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QWp-44-y8i">
|
||||
<rect key="frame" x="10" y="562" width="394" height="300"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="750" constant="300" id="KJn-4o-kRn"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="394" id="R8B-L1-f8s"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="394" id="XB0-Pe-HuZ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="53q-mu-x6X" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="6Vb-zr-n6S"/>
|
||||
<constraint firstAttribute="trailing" secondItem="53q-mu-x6X" secondAttribute="trailing" id="EZg-k9-QuF"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="QWp-44-y8i" secondAttribute="trailing" constant="10" id="GN0-Hk-Sfk"/>
|
||||
<constraint firstItem="QWp-44-y8i" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="10" id="LpX-Ga-7HY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="53q-mu-x6X" secondAttribute="bottom" id="NoA-lJ-53P"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="QWp-44-y8i" secondAttribute="bottom" id="Pvh-aU-qnh"/>
|
||||
<constraint firstItem="QWp-44-y8i" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="YkU-c0-Cgo"/>
|
||||
<constraint firstItem="53q-mu-x6X" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="lJ8-Oz-pLZ"/>
|
||||
<constraint firstItem="QWp-44-y8i" firstAttribute="top" relation="greaterThanOrEqual" secondItem="vUN-kp-3ea" secondAttribute="top" constant="50" id="nnr-Lr-vpY"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<connections>
|
||||
<outlet property="contentView" destination="QWp-44-y8i" id="mFc-eZ-nx2"/>
|
||||
<outlet property="contentViewBottomConstraint" destination="Pvh-aU-qnh" id="U4M-ey-LnG"/>
|
||||
<outlet property="contentViewHeightConstraint" destination="KJn-4o-kRn" id="VUT-sf-ebZ"/>
|
||||
<outlet property="dimmingView" destination="53q-mu-x6X" id="L4V-r2-NUX"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Empty view controller used to embed a view conforming to `SlidingModalPresentable`.
|
||||
final class SlidingModalEmptyViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var modalView: SlidingModalPresentable.ViewType!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate(with view: SlidingModalPresentable.ViewType) -> SlidingModalEmptyViewController {
|
||||
let viewController = SlidingModalEmptyViewController()
|
||||
viewController.modalView = view
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func loadView() {
|
||||
self.view = self.modalView
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SlidingModalPresentable
|
||||
extension SlidingModalEmptyViewController: SlidingModalPresentable {
|
||||
|
||||
func allowsDismissOnBackgroundTap() -> Bool {
|
||||
return self.modalView.allowsDismissOnBackgroundTap()
|
||||
}
|
||||
|
||||
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
|
||||
return self.modalView.layoutHeightFittingWidth(width)
|
||||
}
|
||||
}
|
29
Riot/Modules/SlidingModal/SlidingModalPresentable.swift
Normal file
29
Riot/Modules/SlidingModal/SlidingModalPresentable.swift
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/// `SlidingModalPresentable` is a protocol describing a UI element to present modally using `SlidingModalPresenter`.
|
||||
@objc protocol SlidingModalPresentable {
|
||||
|
||||
typealias ViewType = UIView & SlidingModalPresentable
|
||||
|
||||
typealias ViewControllerType = UIViewController & SlidingModalPresentable
|
||||
|
||||
func allowsDismissOnBackgroundTap() -> Bool
|
||||
|
||||
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat
|
||||
}
|
135
Riot/Modules/SlidingModal/SlidingModalPresentationAnimator.swift
Normal file
135
Riot/Modules/SlidingModal/SlidingModalPresentationAnimator.swift
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/// `SlidingModalPresentationAnimator` handles the animations for a custom sliding view controller transition.
|
||||
final class SlidingModalPresentationAnimator: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum AnimationDuration {
|
||||
static let presentation: TimeInterval = 0.2
|
||||
static let dismissal: TimeInterval = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let isPresenting: Bool
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// Instantiate a SlidingModalPresentationAnimator object.
|
||||
///
|
||||
/// - Parameter isPresenting: true to animate presentation or false to animate dismissal
|
||||
required public init(isPresenting: Bool) {
|
||||
self.isPresenting = isPresenting
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// Animate presented view controller presentation
|
||||
private func animatePresentation(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let presentedViewController = transitionContext.viewController(forKey: .to),
|
||||
let sourceViewController = transitionContext.viewController(forKey: .from) else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let presentedViewControllerView = presentedViewController.view else {
|
||||
return
|
||||
}
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
|
||||
let slidingModalContainerView = SlidingModalContainerView.instantiate()
|
||||
slidingModalContainerView.alpha = 0
|
||||
slidingModalContainerView.updateDimmingViewAlpha(0.0)
|
||||
|
||||
// Add presented view controller view to slidingModalContainerView content view
|
||||
slidingModalContainerView.setContentView(presentedViewControllerView)
|
||||
|
||||
// Add slidingModalContainerView to container view
|
||||
containerView.vc_addSubViewMatchingParent(slidingModalContainerView)
|
||||
containerView.layoutIfNeeded()
|
||||
|
||||
// Adapt slidingModalContainerView content view height from presentedViewControllerView height
|
||||
if let slidingModalPresentable = presentedViewController as? SlidingModalPresentable {
|
||||
let slidingModalContainerViewContentViewWidth = slidingModalContainerView.contentViewFrame.width
|
||||
let presentableHeight = slidingModalPresentable.layoutHeightFittingWidth(slidingModalContainerViewContentViewWidth)
|
||||
slidingModalContainerView.updateContentViewMaxHeight(presentableHeight)
|
||||
slidingModalContainerView.updateContentViewLayout()
|
||||
}
|
||||
|
||||
// Hide slidingModalContainerView content view
|
||||
slidingModalContainerView.prepareDismissAnimation()
|
||||
containerView.layoutIfNeeded()
|
||||
|
||||
let animationDuration = self.transitionDuration(using: transitionContext)
|
||||
|
||||
slidingModalContainerView.preparePresentAnimation()
|
||||
slidingModalContainerView.alpha = 1
|
||||
|
||||
UIView.animate(withDuration: animationDuration, animations: {
|
||||
containerView.layoutIfNeeded()
|
||||
slidingModalContainerView.updateDimmingViewAlpha(1.0)
|
||||
}, completion: { completed in
|
||||
transitionContext.completeTransition(completed)
|
||||
})
|
||||
}
|
||||
|
||||
// Animate presented view controller dismissal
|
||||
private func animateDismissal(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
|
||||
let slidingModalContainerView = self.slidingModalContainerView(from: transitionContext)
|
||||
|
||||
let animationDuration = self.transitionDuration(using: transitionContext)
|
||||
|
||||
slidingModalContainerView?.prepareDismissAnimation()
|
||||
|
||||
UIView.animate(withDuration: animationDuration, animations: {
|
||||
containerView.layoutIfNeeded()
|
||||
slidingModalContainerView?.updateDimmingViewAlpha(0.0)
|
||||
}, completion: { completed in
|
||||
transitionContext.completeTransition(completed)
|
||||
})
|
||||
}
|
||||
|
||||
private func slidingModalContainerView(from transitionContext: UIViewControllerContextTransitioning) -> SlidingModalContainerView? {
|
||||
let modalContentView = transitionContext.containerView.subviews.first(where: { view -> Bool in
|
||||
view is SlidingModalContainerView
|
||||
}) as? SlidingModalContainerView
|
||||
return modalContentView
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIViewControllerAnimatedTransitioning
|
||||
extension SlidingModalPresentationAnimator: UIViewControllerAnimatedTransitioning {
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return self.isPresenting ? AnimationDuration.presentation : AnimationDuration.dismissal
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
if self.isPresenting {
|
||||
self.animatePresentation(using: transitionContext)
|
||||
} else {
|
||||
self.animateDismissal(using: transitionContext)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/// `SlidingModalPresentationController` handles sliding transition presentation life cycle.
|
||||
final class SlidingModalPresentationController: UIPresentationController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var slidingModalContainerView: SlidingModalContainerView? {
|
||||
return self.containerView?.subviews.first(where: { (view) -> Bool in
|
||||
view is SlidingModalContainerView
|
||||
}) as? SlidingModalContainerView
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
if let slidingModalPresentable = self.presentedViewController as? SlidingModalPresentable, let slidingModalContainerView = self.slidingModalContainerView {
|
||||
|
||||
let slidingModalContainerViewContentViewWidth = slidingModalContainerView.contentViewWidthFittingSize(size)
|
||||
|
||||
let presentableHeight = slidingModalPresentable.layoutHeightFittingWidth(slidingModalContainerViewContentViewWidth)
|
||||
slidingModalContainerView.updateContentViewMaxHeight(presentableHeight)
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
|
||||
self.slidingModalContainerView?.updateContentViewLayout()
|
||||
}, completion: { _ in
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
guard let coordinator = presentedViewController.transitionCoordinator else {
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: { [weak self] _ in
|
||||
// Update status bar appearance of presented view controller (if presenting view controller allowed it, see `modalPresentationCapturesStatusBarAppearance` property)
|
||||
self?.presentedViewController.setNeedsStatusBarAppearanceUpdate()
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
self.slidingModalContainerView?.delegate = self
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
guard let coordinator = presentedViewController.transitionCoordinator else {
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: { [weak self] _ -> Void in
|
||||
// Update status bar appearance of presenting view controller
|
||||
self?.presentingViewController.setNeedsStatusBarAppearanceUpdate()
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
if completed {
|
||||
self.slidingModalContainerView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SlidingModalContainerViewDelegate
|
||||
extension SlidingModalPresentationController: SlidingModalContainerViewDelegate {
|
||||
|
||||
func slidingModalContainerViewDidTapBackground(_ view: SlidingModalContainerView) {
|
||||
|
||||
let isDismissOnBackgroundTapAllowed: Bool
|
||||
|
||||
if let slidingModalPresentable = self.presentedViewController as? SlidingModalPresentable {
|
||||
isDismissOnBackgroundTapAllowed = slidingModalPresentable.allowsDismissOnBackgroundTap()
|
||||
} else {
|
||||
isDismissOnBackgroundTapAllowed = true
|
||||
}
|
||||
|
||||
if isDismissOnBackgroundTapAllowed {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/// `SlidingModalPresentationDelegate` handle a custom sliding UIViewController transition.
|
||||
public class SlidingModalPresentationDelegate: NSObject {
|
||||
}
|
||||
|
||||
// MARK: - UIViewControllerTransitioningDelegate
|
||||
extension SlidingModalPresentationDelegate: UIViewControllerTransitioningDelegate {
|
||||
|
||||
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return SlidingModalPresentationAnimator(isPresenting: true)
|
||||
}
|
||||
|
||||
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return SlidingModalPresentationAnimator(isPresenting: false)
|
||||
}
|
||||
|
||||
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
let controller = SlidingModalPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
controller.delegate = self
|
||||
return controller
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
extension SlidingModalPresentationDelegate: UIAdaptivePresentationControllerDelegate {
|
||||
|
||||
// Do not adapt to size classes
|
||||
public func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||
return .none
|
||||
}
|
||||
}
|
72
Riot/Modules/SlidingModal/SlidingModalPresenter.swift
Normal file
72
Riot/Modules/SlidingModal/SlidingModalPresenter.swift
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
/// `SlidingModalPresenter` allows to present a custom UIViewController or UIView conforming to `SlidingModalPresentable` as a modal with a vertical sliding animation from a UIViewController.
|
||||
final class SlidingModalPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum TabletContentSize {
|
||||
static let preferred = CGSize(width: 400.0, height: 400.0)
|
||||
static let minHeight: CGFloat = 0.0
|
||||
static let maxHeight: CGFloat = 600.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// swiftlint:disable weak_delegate
|
||||
private var transitionDelegate: SlidingModalPresentationDelegate?
|
||||
// swiftlint:enable weak_delegate
|
||||
private weak var presentingViewController: UIViewController?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc func present(_ viewController: SlidingModalPresentable.ViewControllerType, from presentingViewController: UIViewController, animated: Bool, completion: (() -> Void)?) {
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
viewController.modalPresentationStyle = .formSheet
|
||||
|
||||
let preferredHeight = viewController.layoutHeightFittingWidth(TabletContentSize.preferred.width).clamped(to: TabletContentSize.minHeight...TabletContentSize.maxHeight)
|
||||
|
||||
viewController.preferredContentSize = CGSize(width: TabletContentSize.preferred.width, height: preferredHeight)
|
||||
} else {
|
||||
let transitionDelegate = SlidingModalPresentationDelegate()
|
||||
|
||||
viewController.modalPresentationStyle = .custom
|
||||
viewController.transitioningDelegate = transitionDelegate
|
||||
|
||||
// Presented view controller does not affect the statusbar appearance
|
||||
viewController.modalPresentationCapturesStatusBarAppearance = false
|
||||
|
||||
self.transitionDelegate = transitionDelegate
|
||||
}
|
||||
|
||||
presentingViewController.present(viewController, animated: animated, completion: completion)
|
||||
|
||||
self.presentingViewController = presentingViewController
|
||||
}
|
||||
|
||||
@objc func presentView(_ view: SlidingModalPresentable.ViewType, from viewControllerPresenter: UIViewController, animated: Bool, completion: (() -> Void)?) {
|
||||
let viewController = SlidingModalEmptyViewController.instantiate(with: view)
|
||||
self.present(viewController, from: viewControllerPresenter, animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
@objc func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
self.presentingViewController?.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
}
|
|
@ -41,14 +41,17 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
// MARK: - Public
|
||||
|
||||
func present(_ module: Presentable, animated: Bool = true) {
|
||||
NSLog("[NavigationRouter] Present \(module)")
|
||||
navigationController.present(module.toPresentable(), animated: animated, completion: nil)
|
||||
}
|
||||
|
||||
func dismissModule(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
NSLog("[NavigationRouter] Dismiss presented module")
|
||||
navigationController.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
func setRootModule(_ module: Presentable, hideNavigationBar: Bool = false, animated: Bool = false, popCompletion: (() -> Void)? = nil) {
|
||||
NSLog("[NavigationRouter] Set root module \(module)")
|
||||
|
||||
let controller = module.toPresentable()
|
||||
|
||||
|
@ -58,7 +61,9 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
}
|
||||
|
||||
// Call all completions so all coordinators can be deallocated
|
||||
completions.forEach { $0.value() }
|
||||
for presentable in completions.keys {
|
||||
runCompletion(for: presentable)
|
||||
}
|
||||
|
||||
if let popCompletion = popCompletion {
|
||||
completions[controller] = popCompletion
|
||||
|
@ -69,18 +74,23 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
}
|
||||
|
||||
func popToRootModule(animated: Bool) {
|
||||
NSLog("[NavigationRouter] Pop to root module")
|
||||
|
||||
if let controllers = navigationController.popToRootViewController(animated: animated) {
|
||||
controllers.forEach { runCompletion(for: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
func popToModule(_ module: Presentable, animated: Bool) {
|
||||
NSLog("[NavigationRouter] Pop to module \(module)")
|
||||
|
||||
if let controllers = navigationController.popToViewController(module.toPresentable(), animated: animated) {
|
||||
controllers.forEach { runCompletion(for: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
func push(_ module: Presentable, animated: Bool = true, popCompletion: (() -> Void)? = nil) {
|
||||
NSLog("[NavigationRouter] Push module \(module)")
|
||||
|
||||
let controller = module.toPresentable()
|
||||
|
||||
|
@ -97,6 +107,8 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
}
|
||||
|
||||
func popModule(animated: Bool = true) {
|
||||
NSLog("[NavigationRouter] Pop module")
|
||||
|
||||
if let controller = navigationController.popViewController(animated: animated) {
|
||||
runCompletion(for: controller)
|
||||
}
|
||||
|
@ -111,7 +123,9 @@ final class NavigationRouter: NSObject, NavigationRouterType {
|
|||
// MARK: - Private
|
||||
|
||||
private func runCompletion(for controller: UIViewController) {
|
||||
guard let completion = completions[controller] else { return }
|
||||
guard let completion = completions[controller] else {
|
||||
return
|
||||
}
|
||||
completion()
|
||||
completions.removeValue(forKey: controller)
|
||||
}
|
||||
|
@ -128,6 +142,8 @@ extension NavigationRouter: UINavigationControllerDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
NSLog("[NavigationRouter] Poppped module: \(poppedViewController)")
|
||||
|
||||
runCompletion(for: poppedViewController)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<true/>
|
||||
<key>ITSEncryptionExportComplianceCode</key>
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.10.2</string>
|
||||
<string>0.10.4</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
|
|
Loading…
Reference in a new issue