mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Finish v0.9.2
This commit is contained in:
commit
5a30474f4d
116 changed files with 5066 additions and 1261 deletions
17
CHANGES.rst
17
CHANGES.rst
|
@ -1,3 +1,20 @@
|
|||
Changes in 0.9.2 (2019-08-08)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* Upgrade MatrixKit version ([v0.10.2](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.10.2)).
|
||||
* Soft logout: Support soft logout (#2540).
|
||||
* Reactions: Emoji picker (#2370).
|
||||
* Widgets: Whitelist https://scalar-staging.vector.im/api (#2612).
|
||||
* Reactions: Show who reacted (#2591).
|
||||
* Media picking: Use native camera and use separate actions for camera and media picker (#638).
|
||||
|
||||
Bug fix:
|
||||
* Crash when leaving settings due to backup section refresh animation.
|
||||
* Reactions: Do not display reactions on redacted events in timeline.
|
||||
* Fix crash for search bar customisation in iOS13 (#2626).
|
||||
* Build: Fix build based on git tag.
|
||||
|
||||
Changes in 0.9.1 (2019-07-17)
|
||||
===============================================
|
||||
|
||||
|
|
6
Podfile
6
Podfile
|
@ -7,7 +7,7 @@ use_frameworks!
|
|||
|
||||
# Different flavours of pods to MatrixKit
|
||||
# The current MatrixKit pod version
|
||||
$matrixKitVersion = '0.10.1'
|
||||
$matrixKitVersion = '0.10.2'
|
||||
|
||||
# The develop branch version
|
||||
#$matrixKitVersion = 'develop'
|
||||
|
@ -79,6 +79,10 @@ abstract_target 'RiotPods' do
|
|||
target "Riot" do
|
||||
import_MatrixKit
|
||||
pod 'DGCollectionViewLeftAlignFlowLayout', '~> 1.0.4'
|
||||
|
||||
target 'RiotTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
target "RiotShareExtension" do
|
||||
|
|
34
Podfile.lock
34
Podfile.lock
|
@ -49,41 +49,41 @@ PODS:
|
|||
- MatomoTracker (6.0.1):
|
||||
- MatomoTracker/Core (= 6.0.1)
|
||||
- MatomoTracker/Core (6.0.1)
|
||||
- MatrixKit (0.10.1):
|
||||
- MatrixKit (0.10.2):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.10.1)
|
||||
- MatrixSDK (= 0.13.0)
|
||||
- MatrixKit/Core (= 0.10.2)
|
||||
- MatrixSDK (= 0.13.1)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixKit/AppExtension (0.10.1):
|
||||
- MatrixKit/AppExtension (0.10.2):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- DTCoreText/Extension
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.13.0)
|
||||
- MatrixSDK (= 0.13.1)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixKit/Core (0.10.1):
|
||||
- MatrixKit/Core (0.10.2):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.13.0)
|
||||
- MatrixSDK (= 0.13.1)
|
||||
- SwiftUTI (~> 1.0.6)
|
||||
- MatrixSDK (0.13.0):
|
||||
- MatrixSDK/Core (= 0.13.0)
|
||||
- MatrixSDK/Core (0.13.0):
|
||||
- MatrixSDK (0.13.1):
|
||||
- MatrixSDK/Core (= 0.13.1)
|
||||
- MatrixSDK/Core (0.13.1):
|
||||
- AFNetworking (~> 3.2.0)
|
||||
- GZIP (~> 1.2.2)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.1.0)
|
||||
- Realm (~> 3.13.1)
|
||||
- MatrixSDK/JingleCallStack (0.13.0):
|
||||
- MatrixSDK/JingleCallStack (0.13.1):
|
||||
- JitsiMeetSDK (~> 2.1.0)
|
||||
- MatrixSDK/Core
|
||||
- MatrixSDK/SwiftSupport (0.13.0):
|
||||
- MatrixSDK/SwiftSupport (0.13.1):
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.1.0):
|
||||
- OLMKit/olmc (= 3.1.0)
|
||||
|
@ -109,8 +109,8 @@ DEPENDENCIES:
|
|||
- DTCoreText
|
||||
- GBDeviceInfo (~> 5.2.0)
|
||||
- MatomoTracker (~> 6.0.1)
|
||||
- MatrixKit (= 0.10.1)
|
||||
- MatrixKit/AppExtension (= 0.10.1)
|
||||
- MatrixKit (= 0.10.2)
|
||||
- MatrixKit/AppExtension (= 0.10.2)
|
||||
- MatrixSDK/JingleCallStack
|
||||
- MatrixSDK/SwiftSupport
|
||||
- OLMKit
|
||||
|
@ -166,8 +166,8 @@ SPEC CHECKSUMS:
|
|||
libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd
|
||||
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
|
||||
MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5
|
||||
MatrixKit: f8224de32ca8b6e4c54a2654369cedec7744dc6d
|
||||
MatrixSDK: 6886e7234c650408db5876b44a7f7608c865ce30
|
||||
MatrixKit: 208801ba995442a6daa913eb6d639bb918c91c24
|
||||
MatrixSDK: 16c8c4b530a7f9fb264baced52b6b0948b1d0a98
|
||||
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
|
||||
Realm: 50071da38fe079e0735e47c9f2eae738c68c5996
|
||||
Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b
|
||||
|
@ -176,6 +176,6 @@ SPEC CHECKSUMS:
|
|||
SwiftUTI: 917993c124f8eac25e88ced0202fc58d7eb50fa8
|
||||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
|
||||
PODFILE CHECKSUM: 6b3ff49b9c446763a5629e71bdde3fe8da7ba93f
|
||||
PODFILE CHECKSUM: 131195037301d9bfb7465da86b3ea79aac686b74
|
||||
|
||||
COCOAPODS: 1.7.2
|
||||
|
|
|
@ -101,6 +101,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 */; };
|
||||
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 */; };
|
||||
926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */; };
|
||||
|
@ -144,6 +145,7 @@
|
|||
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 */; };
|
||||
B120863722EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B120863622EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.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 */; };
|
||||
|
@ -163,6 +165,8 @@
|
|||
B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142B22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift */; };
|
||||
B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142C22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift */; };
|
||||
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */; };
|
||||
B152C73122DF561E0041315A /* EmojiServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B152C73022DF561E0041315A /* EmojiServiceTests.swift */; };
|
||||
B152C73522DF599C0041315A /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = B152C73422DF599B0041315A /* apple_emojis_data.json */; };
|
||||
B1664BC520F4E67600808783 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1664BAD20F4E67500808783 /* FallbackViewController.xib */; };
|
||||
B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BAE20F4E67500808783 /* FallbackViewController.m */; };
|
||||
B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BB220F4E67500808783 /* SharePresentingViewController.m */; };
|
||||
|
@ -457,6 +461,25 @@
|
|||
B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5598D20EFC5E400210D55 /* DecryptionFailure.m */; };
|
||||
B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */; };
|
||||
B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */; };
|
||||
B1B9DEDA22E9B7350065E677 /* SerializationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DED922E9B7350065E677 /* SerializationService.swift */; };
|
||||
B1B9DEDC22E9B7440065E677 /* SerializationServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEDB22E9B7440065E677 /* SerializationServiceType.swift */; };
|
||||
B1B9DEDE22E9D9890065E677 /* EmojiServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEDD22E9D9890065E677 /* EmojiServiceType.swift */; };
|
||||
B1B9DEE822EB34EF0065E677 /* ReactionHistoryCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE022EB34ED0065E677 /* ReactionHistoryCoordinatorType.swift */; };
|
||||
B1B9DEE922EB34EF0065E677 /* ReactionHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE122EB34EE0065E677 /* ReactionHistoryViewController.swift */; };
|
||||
B1B9DEEA22EB34EF0065E677 /* ReactionHistoryViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1B9DEE222EB34EE0065E677 /* ReactionHistoryViewController.storyboard */; };
|
||||
B1B9DEEB22EB34EF0065E677 /* ReactionHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE322EB34EE0065E677 /* ReactionHistoryViewModel.swift */; };
|
||||
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE422EB34EE0065E677 /* ReactionHistoryViewModelType.swift */; };
|
||||
B1B9DEED22EB34EF0065E677 /* ReactionHistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE522EB34EE0065E677 /* ReactionHistoryCoordinator.swift */; };
|
||||
B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE622EB34EE0065E677 /* ReactionHistoryViewAction.swift */; };
|
||||
B1B9DEEF22EB34EF0065E677 /* ReactionHistoryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B9DEE722EB34EE0065E677 /* ReactionHistoryViewState.swift */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C3360022F1ED600021BA8D /* MediaPickerCoordinator.swift */; };
|
||||
B1C3361C22F32B4A0021BA8D /* SingleImagePickerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C3361B22F32B4A0021BA8D /* SingleImagePickerPresenter.swift */; };
|
||||
B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */; };
|
||||
B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562CB228AB3510037F12A /* UIStackView.swift */; };
|
||||
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */; };
|
||||
|
@ -489,6 +512,30 @@
|
|||
B1DB4F0C2231494F0065DBFA /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DB4F0A223131600065DBFA /* String.swift */; };
|
||||
B1DB4F0E22316FFF0065DBFA /* UserNameColorGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DB4F0D22316FFF0065DBFA /* UserNameColorGenerator.swift */; };
|
||||
B1DB4F0F223170000065DBFA /* UserNameColorGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DB4F0D22316FFF0065DBFA /* UserNameColorGenerator.swift */; };
|
||||
B1DCC61722E5E17100625807 /* EmojiPickerViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1DCC60F22E5E16900625807 /* EmojiPickerViewController.storyboard */; };
|
||||
B1DCC61822E5E17100625807 /* EmojiPickerViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61022E5E16900625807 /* EmojiPickerViewState.swift */; };
|
||||
B1DCC61922E5E17100625807 /* EmojiPickerCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61122E5E16A00625807 /* EmojiPickerCoordinatorType.swift */; };
|
||||
B1DCC61A22E5E17100625807 /* EmojiPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61222E5E16C00625807 /* EmojiPickerViewController.swift */; };
|
||||
B1DCC61B22E5E17100625807 /* EmojiPickerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61322E5E16D00625807 /* EmojiPickerCoordinator.swift */; };
|
||||
B1DCC61C22E5E17100625807 /* EmojiPickerViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61422E5E16E00625807 /* EmojiPickerViewAction.swift */; };
|
||||
B1DCC61D22E5E17100625807 /* EmojiPickerViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61522E5E16F00625807 /* EmojiPickerViewModelType.swift */; };
|
||||
B1DCC61E22E5E17100625807 /* EmojiPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61622E5E17000625807 /* EmojiPickerViewModel.swift */; };
|
||||
B1DCC62022E5EDA400625807 /* EmojiPickerCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC61F22E5EDA400625807 /* EmojiPickerCoordinatorBridgePresenter.swift */; };
|
||||
B1DCC62222E60BE000625807 /* EmojiPickerItemViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62122E60BE000625807 /* EmojiPickerItemViewData.swift */; };
|
||||
B1DCC62422E60CA900625807 /* EmojiPickerCategoryViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62322E60CA800625807 /* EmojiPickerCategoryViewData.swift */; };
|
||||
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62522E60CC600625807 /* EmojiItem.swift */; };
|
||||
B1DCC62822E60CE300625807 /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62722E60CE300625807 /* EmojiCategory.swift */; };
|
||||
B1DCC62A22E60D1000625807 /* EmojiMartService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62922E60D1000625807 /* EmojiMartService.swift */; };
|
||||
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62B22E61EAF00625807 /* EmojiPickerViewCell.swift */; };
|
||||
B1DCC62E22E61EAF00625807 /* EmojiPickerViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1DCC62C22E61EAF00625807 /* EmojiPickerViewCell.xib */; };
|
||||
B1DCC63122E7026F00625807 /* EmojiPickerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62F22E7026F00625807 /* EmojiPickerHeaderView.swift */; };
|
||||
B1DCC63222E7026F00625807 /* EmojiPickerHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1DCC63022E7026F00625807 /* EmojiPickerHeaderView.xib */; };
|
||||
B1DCC63422E72C1B00625807 /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63322E72C1B00625807 /* UISearchBar.swift */; };
|
||||
B1DCC63522E72D2200625807 /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63322E72C1B00625807 /* UISearchBar.swift */; };
|
||||
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63622E8541700625807 /* EmojiStore.swift */; };
|
||||
B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */; };
|
||||
B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */; };
|
||||
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */; };
|
||||
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368821FB1E20001F3AFF /* UIButton.swift */; };
|
||||
B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */; };
|
||||
B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */; };
|
||||
|
@ -558,6 +605,7 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1ACF09217ADF1D7E7A35BC02 /* Pods_RiotPods_Riot.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_Riot.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RiotShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3209451121F1C1430088CAA2 /* BlackTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackTheme.swift; sourceTree = "<group>"; };
|
||||
|
@ -679,6 +727,7 @@
|
|||
92726A501F587410004AD26F /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
|
||||
97151D7F0F892081250D50A3 /* Pods_RiotPods_RiotShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_RiotShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9C3242E3FE95BCDA9562C75D /* Pods-RiotPods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
AC34BF67FD21A9D01C16AE5D /* Pods-RiotTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotTests.release.xcconfig"; path = "Target Support Files/Pods-RiotTests/Pods-RiotTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B104C2932203773B00D9F496 /* KeyBackupBannerPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupBannerPreferences.swift; sourceTree = "<group>"; };
|
||||
B1057788221304EB00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupSuccessFromPassphraseViewController.swift; sourceTree = "<group>"; };
|
||||
B105778A221304FA00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupSetupSuccessFromPassphraseViewController.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -715,6 +764,7 @@
|
|||
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>"; };
|
||||
B120863622EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionHistoryBridgeCoordinatorPresenter.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>"; };
|
||||
|
@ -734,6 +784,9 @@
|
|||
B14F142B22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModel.swift; sourceTree = "<group>"; };
|
||||
B14F142C22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewAction.swift; sourceTree = "<group>"; };
|
||||
B14F142D22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewController.swift; sourceTree = "<group>"; };
|
||||
B152C72D22DF55D80041315A /* RiotTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RiotTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
B152C73022DF561E0041315A /* EmojiServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiServiceTests.swift; sourceTree = "<group>"; };
|
||||
B152C73422DF599B0041315A /* apple_emojis_data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
||||
B1664BAD20F4E67500808783 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = "<group>"; };
|
||||
B1664BAE20F4E67500808783 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = "<group>"; };
|
||||
B1664BAF20F4E67500808783 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -1212,6 +1265,25 @@
|
|||
B1B5599020EFC5E400210D55 /* Analytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = "<group>"; };
|
||||
B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DecryptionFailureTracker.m; sourceTree = "<group>"; };
|
||||
B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RoomPredecessorBubbleCell.xib; sourceTree = "<group>"; };
|
||||
B1B9DED922E9B7350065E677 /* SerializationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializationService.swift; sourceTree = "<group>"; };
|
||||
B1B9DEDB22E9B7440065E677 /* SerializationServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializationServiceType.swift; sourceTree = "<group>"; };
|
||||
B1B9DEDD22E9D9890065E677 /* EmojiServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiServiceType.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE022EB34ED0065E677 /* ReactionHistoryCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE122EB34EE0065E677 /* ReactionHistoryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewController.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE222EB34EE0065E677 /* ReactionHistoryViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ReactionHistoryViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1B9DEE322EB34EE0065E677 /* ReactionHistoryViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewModel.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE422EB34EE0065E677 /* ReactionHistoryViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewModelType.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE522EB34EE0065E677 /* ReactionHistoryCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE622EB34EE0065E677 /* ReactionHistoryViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewAction.swift; sourceTree = "<group>"; };
|
||||
B1B9DEE722EB34EE0065E677 /* ReactionHistoryViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionHistoryViewState.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
B1C3360022F1ED600021BA8D /* MediaPickerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPickerCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1C3361B22F32B4A0021BA8D /* SingleImagePickerPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleImagePickerPresenter.swift; sourceTree = "<group>"; };
|
||||
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
B1C562CB228AB3510037F12A /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
|
||||
B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuItem.swift; sourceTree = "<group>"; };
|
||||
|
@ -1242,11 +1314,35 @@
|
|||
B1DB4F05223015080065DBFA /* Character.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Character.swift; sourceTree = "<group>"; };
|
||||
B1DB4F0A223131600065DBFA /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
B1DB4F0D22316FFF0065DBFA /* UserNameColorGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNameColorGenerator.swift; sourceTree = "<group>"; };
|
||||
B1DCC60F22E5E16900625807 /* EmojiPickerViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = EmojiPickerViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1DCC61022E5E16900625807 /* EmojiPickerViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewState.swift; sourceTree = "<group>"; };
|
||||
B1DCC61122E5E16A00625807 /* EmojiPickerCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1DCC61222E5E16C00625807 /* EmojiPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewController.swift; sourceTree = "<group>"; };
|
||||
B1DCC61322E5E16D00625807 /* EmojiPickerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1DCC61422E5E16E00625807 /* EmojiPickerViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewAction.swift; sourceTree = "<group>"; };
|
||||
B1DCC61522E5E16F00625807 /* EmojiPickerViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewModelType.swift; sourceTree = "<group>"; };
|
||||
B1DCC61622E5E17000625807 /* EmojiPickerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewModel.swift; sourceTree = "<group>"; };
|
||||
B1DCC61F22E5EDA400625807 /* EmojiPickerCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B1DCC62122E60BE000625807 /* EmojiPickerItemViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerItemViewData.swift; sourceTree = "<group>"; };
|
||||
B1DCC62322E60CA800625807 /* EmojiPickerCategoryViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerCategoryViewData.swift; sourceTree = "<group>"; };
|
||||
B1DCC62522E60CC600625807 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
||||
B1DCC62722E60CE300625807 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = "<group>"; };
|
||||
B1DCC62922E60D1000625807 /* EmojiMartService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartService.swift; sourceTree = "<group>"; };
|
||||
B1DCC62B22E61EAF00625807 /* EmojiPickerViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewCell.swift; sourceTree = "<group>"; };
|
||||
B1DCC62C22E61EAF00625807 /* EmojiPickerViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiPickerViewCell.xib; sourceTree = "<group>"; };
|
||||
B1DCC62F22E7026F00625807 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; };
|
||||
B1DCC63022E7026F00625807 /* EmojiPickerHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiPickerHeaderView.xib; sourceTree = "<group>"; };
|
||||
B1DCC63322E72C1B00625807 /* UISearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISearchBar.swift; sourceTree = "<group>"; };
|
||||
B1DCC63622E8541700625807 /* EmojiStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiStore.swift; sourceTree = "<group>"; };
|
||||
B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartStore.swift; sourceTree = "<group>"; };
|
||||
B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartCategory.swift; sourceTree = "<group>"; };
|
||||
B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EmojiItem+EmojiMart.swift"; sourceTree = "<group>"; };
|
||||
B1E5368821FB1E20001F3AFF /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
|
||||
B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewController.swift; sourceTree = "<group>"; };
|
||||
B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromPassphraseViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1FDF55F21F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupPassphraseViewAction.swift; sourceTree = "<group>"; };
|
||||
B43DC75D1590BB8A4243BD4D /* Pods-RiotPods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-Riot.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.release.xcconfig"; sourceTree = "<group>"; };
|
||||
BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotTests.debug.xcconfig"; path = "Target Support Files/Pods-RiotTests/Pods-RiotTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E2599D0ECB8DD206624E450B /* Pods-RiotPods-SiriIntents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-SiriIntents.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.release.xcconfig"; sourceTree = "<group>"; };
|
||||
F05927C71FDED835009F2A68 /* MXGroup+Riot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MXGroup+Riot.m"; sourceTree = "<group>"; };
|
||||
F05927C81FDED835009F2A68 /* MXGroup+Riot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MXGroup+Riot.h"; sourceTree = "<group>"; };
|
||||
|
@ -1317,6 +1413,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3AF393339D2D566CE14AC200 /* Pods_RiotTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1601,6 +1698,8 @@
|
|||
4FC6A5D63FAD1B27C2F57AFA /* Pods-RiotPods-RiotShareExtension.release.xcconfig */,
|
||||
3942DD65EBEB7AE647C6392A /* Pods-RiotPods-SiriIntents.debug.xcconfig */,
|
||||
E2599D0ECB8DD206624E450B /* Pods-RiotPods-SiriIntents.release.xcconfig */,
|
||||
BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */,
|
||||
AC34BF67FD21A9D01C16AE5D /* Pods-RiotTests.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1612,6 +1711,7 @@
|
|||
1ACF09217ADF1D7E7A35BC02 /* Pods_RiotPods_Riot.framework */,
|
||||
97151D7F0F892081250D50A3 /* Pods_RiotPods_RiotShareExtension.framework */,
|
||||
51187E952D5CECF6D6F5A28E /* Pods_RiotPods_SiriIntents.framework */,
|
||||
129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1762,6 +1862,39 @@
|
|||
path = RecoveryKey;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B152C72922DCEA670041315A /* EmojiPicker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1DCC61F22E5EDA400625807 /* EmojiPickerCoordinatorBridgePresenter.swift */,
|
||||
B1DCC61322E5E16D00625807 /* EmojiPickerCoordinator.swift */,
|
||||
B1DCC61122E5E16A00625807 /* EmojiPickerCoordinatorType.swift */,
|
||||
B1DCC61422E5E16E00625807 /* EmojiPickerViewAction.swift */,
|
||||
B1DCC61222E5E16C00625807 /* EmojiPickerViewController.swift */,
|
||||
B1DCC60F22E5E16900625807 /* EmojiPickerViewController.storyboard */,
|
||||
B1DCC61622E5E17000625807 /* EmojiPickerViewModel.swift */,
|
||||
B1DCC61522E5E16F00625807 /* EmojiPickerViewModelType.swift */,
|
||||
B1DCC61022E5E16900625807 /* EmojiPickerViewState.swift */,
|
||||
B1DCC62322E60CA800625807 /* EmojiPickerCategoryViewData.swift */,
|
||||
B1DCC62122E60BE000625807 /* EmojiPickerItemViewData.swift */,
|
||||
B1DCC62F22E7026F00625807 /* EmojiPickerHeaderView.swift */,
|
||||
B1DCC63022E7026F00625807 /* EmojiPickerHeaderView.xib */,
|
||||
B1DCC62B22E61EAF00625807 /* EmojiPickerViewCell.swift */,
|
||||
B1DCC62C22E61EAF00625807 /* EmojiPickerViewCell.xib */,
|
||||
B152C72C22DF21880041315A /* Data */,
|
||||
);
|
||||
path = EmojiPicker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B152C72C22DF21880041315A /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1B9DEDD22E9D9890065E677 /* EmojiServiceType.swift */,
|
||||
B1DCC64022E9B37400625807 /* Store */,
|
||||
B1B9DED722E9B56C0065E677 /* EmojiMart */,
|
||||
);
|
||||
path = Data;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1664BAB20F4E67500808783 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2064,6 +2197,7 @@
|
|||
B1B556DC20EE6C4C00210D55 /* Call */,
|
||||
B1B5568720EE6C4C00210D55 /* Contacts */,
|
||||
B1B556ED20EE6C4C00210D55 /* MediaPicker */,
|
||||
B1C3361A22F328AE0021BA8D /* Camera */,
|
||||
B1B556E420EE6C4C00210D55 /* UserDevices */,
|
||||
B1B5596B20EFA85C00210D55 /* EncryptionInfo */,
|
||||
B1B556FD20EE6C4C00210D55 /* RoomKeyRequest */,
|
||||
|
@ -2158,6 +2292,8 @@
|
|||
B1B5569020EE6C4C00210D55 /* Settings */,
|
||||
B1C562D7228C0B4C0037F12A /* ContextualMenu */,
|
||||
B1963B24228F1C4800CBA17F /* BubbleReactions */,
|
||||
B152C72922DCEA670041315A /* EmojiPicker */,
|
||||
B1B9DEDF22EB34ED0065E677 /* ReactionHistory */,
|
||||
);
|
||||
path = Room;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2445,11 +2581,15 @@
|
|||
B1B556ED20EE6C4C00210D55 /* MediaPicker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1C335FF22F1ED5F0021BA8D /* MediaPickerCoordinatorBridgePresenter.swift */,
|
||||
B1C335FE22F1ED5F0021BA8D /* MediaPickerCoordinatorType.swift */,
|
||||
B1C3360022F1ED600021BA8D /* MediaPickerCoordinator.swift */,
|
||||
B1B556F320EE6C4C00210D55 /* MediaPickerViewController.h */,
|
||||
B1B556EF20EE6C4C00210D55 /* MediaPickerViewController.m */,
|
||||
B1B556F220EE6C4C00210D55 /* MediaPickerViewController.xib */,
|
||||
B1B557CD20EF5E3500210D55 /* Views */,
|
||||
B1B5577720EE724200210D55 /* Library */,
|
||||
B1B557CD20EF5E3500210D55 /* Views */,
|
||||
B1C3361B22F32B4A0021BA8D /* SingleImagePickerPresenter.swift */,
|
||||
);
|
||||
path = MediaPicker;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3088,6 +3228,7 @@
|
|||
B1B5597C20EFC3DF00210D55 /* Managers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1B9DED822E9B7120065E677 /* Serialization */,
|
||||
B1FDF56321F68C0700BA3834 /* PasswordStrength */,
|
||||
B1798300211B137B001FD722 /* OnBoarding */,
|
||||
B1B5598B20EFC5E400210D55 /* Analytics */,
|
||||
|
@ -3141,6 +3282,53 @@
|
|||
path = Analytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B9DED722E9B56C0065E677 /* EmojiMart */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1DCC62922E60D1000625807 /* EmojiMartService.swift */,
|
||||
B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */,
|
||||
B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */,
|
||||
B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */,
|
||||
);
|
||||
path = EmojiMart;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B9DED822E9B7120065E677 /* Serialization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1B9DEDB22E9B7440065E677 /* SerializationServiceType.swift */,
|
||||
B1B9DED922E9B7350065E677 /* SerializationService.swift */,
|
||||
);
|
||||
path = Serialization;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B9DEDF22EB34ED0065E677 /* ReactionHistory */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B120863622EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift */,
|
||||
B1B9DEE022EB34ED0065E677 /* ReactionHistoryCoordinatorType.swift */,
|
||||
B1B9DEE522EB34EE0065E677 /* ReactionHistoryCoordinator.swift */,
|
||||
B1B9DEE122EB34EE0065E677 /* ReactionHistoryViewController.swift */,
|
||||
B1B9DEE222EB34EE0065E677 /* ReactionHistoryViewController.storyboard */,
|
||||
B1B9DEE422EB34EE0065E677 /* ReactionHistoryViewModelType.swift */,
|
||||
B1B9DEE322EB34EE0065E677 /* ReactionHistoryViewModel.swift */,
|
||||
B1B9DEE622EB34EE0065E677 /* ReactionHistoryViewAction.swift */,
|
||||
B1B9DEE722EB34EE0065E677 /* ReactionHistoryViewState.swift */,
|
||||
B1B9DEF022EB396B0065E677 /* ReactionHistoryViewData.swift */,
|
||||
B1B9DEF222EB426D0065E677 /* ReactionHistoryViewCell.swift */,
|
||||
B1B9DEF322EB426D0065E677 /* ReactionHistoryViewCell.xib */,
|
||||
);
|
||||
path = ReactionHistory;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1C3361A22F328AE0021BA8D /* Camera */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1C335CC22F1C1320021BA8D /* CameraPresenter.swift */,
|
||||
);
|
||||
path = Camera;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1C562D7228C0B4C0037F12A /* ContextualMenu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3183,6 +3371,16 @@
|
|||
path = KeyboardAvoiding;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1DCC64022E9B37400625807 /* Store */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1DCC63622E8541700625807 /* EmojiStore.swift */,
|
||||
B1DCC62722E60CE300625807 /* EmojiCategory.swift */,
|
||||
B1DCC62522E60CC600625807 /* EmojiItem.swift */,
|
||||
);
|
||||
path = Store;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1E5368A21FB6FC0001F3AFF /* Passphrase */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3224,6 +3422,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
F083BB041E7005FD00A9B29C /* RiotTests.m */,
|
||||
B152C73022DF561E0041315A /* EmojiServiceTests.swift */,
|
||||
F083BB071E70067700A9B29C /* Supporting Files */,
|
||||
);
|
||||
path = RiotTests;
|
||||
|
@ -3232,6 +3431,7 @@
|
|||
F083BB071E70067700A9B29C /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B152C72D22DF55D80041315A /* RiotTests-Bridging-Header.h */,
|
||||
F083BB031E7005FD00A9B29C /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
|
@ -3265,6 +3465,7 @@
|
|||
32935CB21F628B98006888C8 /* js */,
|
||||
F083BBEF1E7009EC00A9B29C /* Images.xcassets */,
|
||||
B169328620F3954A00746532 /* SharedImages.xcassets */,
|
||||
B152C73422DF599B0041315A /* apple_emojis_data.json */,
|
||||
B169329320F39E6200746532 /* LaunchScreen.storyboard */,
|
||||
B169329520F39E6300746532 /* Main.storyboard */,
|
||||
F083BC0E1E7009EC00A9B29C /* third_party_licenses.html */,
|
||||
|
@ -3316,6 +3517,7 @@
|
|||
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */,
|
||||
B1C562CB228AB3510037F12A /* UIStackView.swift */,
|
||||
B1B12B2822942315002CB419 /* UITouch.swift */,
|
||||
B1DCC63322E72C1B00625807 /* UISearchBar.swift */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3451,6 +3653,7 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F094A9CB1B78D8F000B1FBBF /* Build configuration list for PBXNativeTarget "RiotTests" */;
|
||||
buildPhases = (
|
||||
CB374E980E51D4490F17AD40 /* [CP] Check Pods Manifest.lock */,
|
||||
F094A9BA1B78D8F000B1FBBF /* Sources */,
|
||||
F094A9BB1B78D8F000B1FBBF /* Frameworks */,
|
||||
F094A9BC1B78D8F000B1FBBF /* Resources */,
|
||||
|
@ -3515,6 +3718,7 @@
|
|||
F094A9BD1B78D8F000B1FBBF = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
DevelopmentTeam = 7J4U792NQT;
|
||||
LastSwiftMigration = 1020;
|
||||
TestTargetID = F094A9A11B78D8F000B1FBBF;
|
||||
};
|
||||
};
|
||||
|
@ -3585,6 +3789,7 @@
|
|||
B1B5590220EF768F00210D55 /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B558CA20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
32891D702264DF7B00C82226 /* DeviceVerificationVerifiedViewController.storyboard in Resources */,
|
||||
B152C73522DF599C0041315A /* apple_emojis_data.json in Resources */,
|
||||
F083BDE91E7009ED00A9B29C /* ring.mp3 in Resources */,
|
||||
B1B558F720EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B1B5592B20EF7A5D00210D55 /* TableViewCellWithButton.xib in Resources */,
|
||||
|
@ -3610,6 +3815,7 @@
|
|||
B1B558F220EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B1B558EA20EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B558CD20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B9DEEA22EB34EF0065E677 /* ReactionHistoryViewController.storyboard in Resources */,
|
||||
B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */,
|
||||
B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */,
|
||||
B1B5572120EE6C4D00210D55 /* ContactsTableViewController.xib in Resources */,
|
||||
|
@ -3641,6 +3847,7 @@
|
|||
B1B5590820EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
F083BDE81E7009ED00A9B29C /* message.mp3 in Resources */,
|
||||
B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */,
|
||||
B1B9DEF522EB426D0065E677 /* ReactionHistoryViewCell.xib in Resources */,
|
||||
B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */,
|
||||
F083BE011E7009ED00A9B29C /* third_party_licenses.html in Resources */,
|
||||
B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */,
|
||||
|
@ -3650,6 +3857,7 @@
|
|||
3232AB2122564D9100AD6A5C /* README.md in Resources */,
|
||||
B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */,
|
||||
B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */,
|
||||
B1DCC61722E5E17100625807 /* EmojiPickerViewController.storyboard in Resources */,
|
||||
32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */,
|
||||
B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */,
|
||||
B1664DA320F4F96200808783 /* Vector.strings in Resources */,
|
||||
|
@ -3667,6 +3875,7 @@
|
|||
3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */,
|
||||
B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */,
|
||||
32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */,
|
||||
B1DCC63222E7026F00625807 /* EmojiPickerHeaderView.xib in Resources */,
|
||||
B1B5578E20EF568D00210D55 /* GroupInviteTableViewCell.xib in Resources */,
|
||||
B1B5582020EF625800210D55 /* SimpleRoomTitleView.xib in Resources */,
|
||||
F083BE061E7009ED00A9B29C /* Riot-Defaults.plist in Resources */,
|
||||
|
@ -3689,6 +3898,7 @@
|
|||
B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */,
|
||||
F083BDF31E7009ED00A9B29C /* Images.xcassets in Resources */,
|
||||
B1C562E4228C7C8D0037F12A /* RoomContextualMenuToolbarView.xib in Resources */,
|
||||
B1DCC62E22E61EAF00625807 /* EmojiPickerViewCell.xib in Resources */,
|
||||
B1B5590720EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B169329920F39E6300746532 /* LaunchScreen.storyboard in Resources */,
|
||||
B1B5595320EF9A8700210D55 /* RecentTableViewCell.xib in Resources */,
|
||||
|
@ -3839,6 +4049,28 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "${PODS_ROOT}/SwiftLint/swiftlint\n";
|
||||
};
|
||||
CB374E980E51D4490F17AD40 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-RiotTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
FF06981FDA0DB688B8C52A41 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -3886,6 +4118,7 @@
|
|||
32242F0A21E8B21300725742 /* UIColor.swift in Sources */,
|
||||
32242F1921E8FBFB00725742 /* DarkTheme.swift in Sources */,
|
||||
B1664BC820F4E67600808783 /* ShareDataSource.m in Sources */,
|
||||
B1DCC63522E72D2200625807 /* UISearchBar.swift in Sources */,
|
||||
B1664BCD20F4E67600808783 /* RecentRoomTableViewCell.m in Sources */,
|
||||
B1DB4F0C2231494F0065DBFA /* String.swift in Sources */,
|
||||
B169331720F3CBE000746532 /* RecentCellData.m in Sources */,
|
||||
|
@ -3917,6 +4150,7 @@
|
|||
B16932A520F3A21C00746532 /* empty.mm in Sources */,
|
||||
3232AB4A2256558300AD6A5C /* FlowTemplateCoordinator.swift in Sources */,
|
||||
B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */,
|
||||
B1B9DEDE22E9D9890065E677 /* EmojiServiceType.swift in Sources */,
|
||||
3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */,
|
||||
B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */,
|
||||
B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */,
|
||||
|
@ -3926,6 +4160,8 @@
|
|||
B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */,
|
||||
B1DB4F0E22316FFF0065DBFA /* UserNameColorGenerator.swift in Sources */,
|
||||
B1057789221304EC00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift in Sources */,
|
||||
B1DCC61922E5E17100625807 /* EmojiPickerCoordinatorType.swift in Sources */,
|
||||
B1C3360122F1ED600021BA8D /* MediaPickerCoordinatorType.swift in Sources */,
|
||||
3232ABB72257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift in Sources */,
|
||||
32F6B96D2270623100BBA352 /* DeviceVerificationDataLoadingViewModel.swift in Sources */,
|
||||
B16932B120F3AC9200746532 /* RoomSearchDataSource.m in Sources */,
|
||||
|
@ -3953,14 +4189,18 @@
|
|||
B1B558F420EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
B1B5572320EE6C4D00210D55 /* AttachmentsViewController.m in Sources */,
|
||||
F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */,
|
||||
B120863722EF375F001F89E0 /* ReactionHistoryBridgeCoordinatorPresenter.swift in Sources */,
|
||||
B1B5598620EFC3E000210D55 /* RiotSettings.swift in Sources */,
|
||||
3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */,
|
||||
3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */,
|
||||
B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */,
|
||||
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */,
|
||||
B1DCC61C22E5E17100625807 /* EmojiPickerViewAction.swift in Sources */,
|
||||
B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */,
|
||||
B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
3232ABC022594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift in Sources */,
|
||||
B1963B2E228F1C4900CBA17F /* BubbleReactionViewData.swift in Sources */,
|
||||
B1C3361C22F32B4A0021BA8D /* SingleImagePickerPresenter.swift in Sources */,
|
||||
B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */,
|
||||
B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
|
||||
|
@ -3970,15 +4210,18 @@
|
|||
B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */,
|
||||
B1098BF621ECFE65000DDA48 /* KeyBackupSetupPassphraseCoordinator.swift in Sources */,
|
||||
B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */,
|
||||
B1B9DEDC22E9B7440065E677 /* SerializationServiceType.swift in Sources */,
|
||||
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */,
|
||||
32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */,
|
||||
B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */,
|
||||
B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */,
|
||||
B1DCC63122E7026F00625807 /* EmojiPickerHeaderView.swift in Sources */,
|
||||
B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */,
|
||||
32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */,
|
||||
B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */,
|
||||
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */,
|
||||
3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */,
|
||||
B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */,
|
||||
|
@ -3995,11 +4238,14 @@
|
|||
B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */,
|
||||
B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */,
|
||||
B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */,
|
||||
B1B9DEEB22EB34EF0065E677 /* ReactionHistoryViewModel.swift in Sources */,
|
||||
B1963B2F228F1C4900CBA17F /* BubbleReactionViewCell.swift in Sources */,
|
||||
B1B558E920EF768F00210D55 /* RoomSelectedStickerBubbleCell.m in Sources */,
|
||||
B1C3360222F1ED600021BA8D /* MediaPickerCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B558DF20EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
F083BE041E7009ED00A9B29C /* Tools.m in Sources */,
|
||||
3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */,
|
||||
B1B9DEE822EB34EF0065E677 /* ReactionHistoryCoordinatorType.swift in Sources */,
|
||||
B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */,
|
||||
B1098C1121ED07E4000DDA48 /* NavigationRouterType.swift in Sources */,
|
||||
B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */,
|
||||
|
@ -4028,7 +4274,9 @@
|
|||
B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */,
|
||||
B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */,
|
||||
B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */,
|
||||
B1B9DEF422EB426D0065E677 /* ReactionHistoryViewCell.swift in Sources */,
|
||||
B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */,
|
||||
B1DCC61B22E5E17100625807 /* EmojiPickerCoordinator.swift in Sources */,
|
||||
32F6B96E2270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift in Sources */,
|
||||
B1B5592C20EF7A5D00210D55 /* TableViewCellWithButton.m in Sources */,
|
||||
32242F1421E8FBA900725742 /* DefaultTheme.swift in Sources */,
|
||||
|
@ -4040,16 +4288,20 @@
|
|||
B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */,
|
||||
3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */,
|
||||
B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */,
|
||||
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */,
|
||||
B1B5572A20EE6C4D00210D55 /* RoomMemberDetailsViewController.m in Sources */,
|
||||
B1B5590120EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */,
|
||||
32F6B96B2270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift in Sources */,
|
||||
B1B558C920EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B1B5571B20EE6C4D00210D55 /* DeactivateAccountViewController.m in Sources */,
|
||||
B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */,
|
||||
B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */,
|
||||
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
|
||||
B1DCC62422E60CA900625807 /* EmojiPickerCategoryViewData.swift in Sources */,
|
||||
324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */,
|
||||
B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */,
|
||||
B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */,
|
||||
3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */,
|
||||
B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */,
|
||||
|
@ -4068,7 +4320,9 @@
|
|||
3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */,
|
||||
B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */,
|
||||
B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */,
|
||||
B1DCC61E22E5E17100625807 /* EmojiPickerViewModel.swift in Sources */,
|
||||
B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */,
|
||||
B1B9DEDA22E9B7350065E677 /* SerializationService.swift in Sources */,
|
||||
B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */,
|
||||
B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */,
|
||||
B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */,
|
||||
|
@ -4076,6 +4330,7 @@
|
|||
B1B12B2922942315002CB419 /* UITouch.swift in Sources */,
|
||||
B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */,
|
||||
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */,
|
||||
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
|
||||
B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */,
|
||||
B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */,
|
||||
|
@ -4086,6 +4341,8 @@
|
|||
B1098BFE21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModelType.swift in Sources */,
|
||||
B1B558BE20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */,
|
||||
F083BDED1E7009ED00A9B29C /* MXKRoomBubbleTableViewCell+Riot.m in Sources */,
|
||||
B1B9DEEF22EB34EF0065E677 /* ReactionHistoryViewState.swift in Sources */,
|
||||
B1DCC62022E5EDA400625807 /* EmojiPickerCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B557A820EF5A1B00210D55 /* DeviceTableViewCell.m in Sources */,
|
||||
B1B5572620EE6C4D00210D55 /* RoomFilesSearchViewController.m in Sources */,
|
||||
B1B5583120EF66BA00210D55 /* RoomIdOrAliasTableViewCell.m in Sources */,
|
||||
|
@ -4094,10 +4351,12 @@
|
|||
F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */,
|
||||
B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */,
|
||||
B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */,
|
||||
B1DCC62822E60CE300625807 /* EmojiCategory.swift in Sources */,
|
||||
B1B5578F20EF568D00210D55 /* GroupTableViewCell.m in Sources */,
|
||||
B1B5573220EE6C4D00210D55 /* GroupHomeViewController.m in Sources */,
|
||||
B1B5595220EF9A8700210D55 /* RecentTableViewCell.m in Sources */,
|
||||
32F6B96C2270623100BBA352 /* DeviceVerificationDataLoadingCoordinatorType.swift in Sources */,
|
||||
B1DCC61D22E5E17100625807 /* EmojiPickerViewModelType.swift in Sources */,
|
||||
B1B5574120EE6C4D00210D55 /* RecentsViewController.m in Sources */,
|
||||
B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */,
|
||||
B1B5577120EE702800210D55 /* StickerPickerViewController.m in Sources */,
|
||||
|
@ -4116,6 +4375,7 @@
|
|||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */,
|
||||
B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */,
|
||||
B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */,
|
||||
B1DCC61A22E5E17100625807 /* EmojiPickerViewController.swift in Sources */,
|
||||
B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */,
|
||||
32A6001722C661100042C1D9 /* EditHistoryViewController.swift in Sources */,
|
||||
B1098BFA21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModel.swift in Sources */,
|
||||
|
@ -4129,6 +4389,7 @@
|
|||
324A2052225FC571004FE8B0 /* DeviceVerificationIncomingViewAction.swift in Sources */,
|
||||
B105778D2213051E00334B1E /* KeyBackupSetupSuccessFromRecoveryKeyViewController.swift in Sources */,
|
||||
B1B557D820EF5EA900210D55 /* RoomActivitiesView.m in Sources */,
|
||||
B1B9DEE922EB34EF0065E677 /* ReactionHistoryViewController.swift in Sources */,
|
||||
B1B5596620EF9E9B00210D55 /* RoomTableViewCell.m in Sources */,
|
||||
B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */,
|
||||
32A6001822C661100042C1D9 /* EditHistoryViewModel.swift in Sources */,
|
||||
|
@ -4144,17 +4405,22 @@
|
|||
B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */,
|
||||
3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */,
|
||||
B1B9DEED22EB34EF0065E677 /* ReactionHistoryCoordinator.swift in Sources */,
|
||||
B1DCC62A22E60D1000625807 /* EmojiMartService.swift in Sources */,
|
||||
B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B558F020EF768F00210D55 /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */,
|
||||
926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */,
|
||||
B1B5593820EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.m in Sources */,
|
||||
B1DCC62222E60BE000625807 /* EmojiPickerItemViewData.swift in Sources */,
|
||||
3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */,
|
||||
B1C335CD22F1C1320021BA8D /* CameraPresenter.swift in Sources */,
|
||||
B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */,
|
||||
B1B557C620EF5CD400210D55 /* DirectoryServerDetailTableViewCell.m in Sources */,
|
||||
B1B5590920EF768F00210D55 /* RoomEmptyBubbleCell.m in Sources */,
|
||||
324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */,
|
||||
3232ABB92257BE6500AD6A5C /* DeviceVerificationVerifyViewController.swift in Sources */,
|
||||
B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */,
|
||||
B1DCC61822E5E17100625807 /* EmojiPickerViewState.swift in Sources */,
|
||||
B1C562DB228C0BB00037F12A /* RoomContextualMenuAction.swift in Sources */,
|
||||
B1B5574720EE6C4D00210D55 /* UsersDevicesViewController.m in Sources */,
|
||||
B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */,
|
||||
|
@ -4174,6 +4440,7 @@
|
|||
B14F143222144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinator.swift in Sources */,
|
||||
B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */,
|
||||
B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */,
|
||||
B1C3360322F1ED600021BA8D /* MediaPickerCoordinator.swift in Sources */,
|
||||
B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */,
|
||||
B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */,
|
||||
B169330320F3C98900746532 /* RoomBubbleCellData.m in Sources */,
|
||||
|
@ -4214,6 +4481,7 @@
|
|||
B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */,
|
||||
B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */,
|
||||
B1B9DEF122EB396B0065E677 /* ReactionHistoryViewData.swift in Sources */,
|
||||
B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
32242F0921E8B05F00725742 /* UIColor.swift in Sources */,
|
||||
B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */,
|
||||
|
@ -4225,10 +4493,13 @@
|
|||
B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */,
|
||||
B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */,
|
||||
B1DB4F06223015080065DBFA /* Character.swift in Sources */,
|
||||
B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */,
|
||||
32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */,
|
||||
B1B9DEEC22EB34EF0065E677 /* ReactionHistoryViewModelType.swift in Sources */,
|
||||
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */,
|
||||
B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */,
|
||||
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */,
|
||||
B1DCC63422E72C1B00625807 /* UISearchBar.swift in Sources */,
|
||||
32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -4238,6 +4509,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F083BEA51E70356E00A9B29C /* RiotTests.m in Sources */,
|
||||
B152C73122DF561E0041315A /* EmojiServiceTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -4664,9 +4936,11 @@
|
|||
};
|
||||
F094A9CC1B78D8F000B1FBBF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
DEVELOPMENT_TEAM = 7J4U792NQT;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
|
@ -4684,15 +4958,20 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.matrix.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "RiotTests/RiotTests-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Riot.app/Riot";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F094A9CD1B78D8F000B1FBBF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AC34BF67FD21A9D01C16AE5D /* Pods-RiotTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
DEVELOPMENT_TEAM = 7J4U792NQT;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
|
@ -4706,6 +4985,8 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.matrix.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "RiotTests/RiotTests-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Riot.app/Riot";
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -2770,7 +2770,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
|||
|
||||
// Remove inApp notifications toggle change
|
||||
MXKAccount *account = notif.object;
|
||||
[account removeObserver:self forKeyPath:@"enableInAppNotifications"];
|
||||
if (!account.isSoftLogout)
|
||||
{
|
||||
[account removeObserver:self forKeyPath:@"enableInAppNotifications"];
|
||||
}
|
||||
|
||||
// Clear Modular data
|
||||
[[WidgetManager sharedManager] deleteDataForUser:account.mxCredentials.userId];
|
||||
|
@ -2781,6 +2784,16 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
|||
[self logoutWithConfirmation:NO completion:nil];
|
||||
}
|
||||
}];
|
||||
|
||||
// Add observer to handle soft logout
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidSoftlogoutAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
MXKAccount *account = notif.object;
|
||||
[self removeMatrixSession:account.mxSession];
|
||||
|
||||
// Return to authentication screen
|
||||
[self.masterTabBarController showAuthenticationScreenAfterSoftLogout:account.mxCredentials];
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionIgnoredUsersDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notif) {
|
||||
|
||||
|
|
23
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/Contents.json
vendored
Normal file
23
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "more_reactions.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "more_reactions@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "more_reactions@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions@2x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions@3x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/more_reactions.imageset/more_reactions@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
|
@ -33,6 +33,7 @@
|
|||
<key>integrationsWidgetsUrls</key>
|
||||
<array>
|
||||
<string>https://scalar-staging.riot.im/scalar/api</string>
|
||||
<string>https://scalar-staging.vector.im/api</string>
|
||||
<string>https://scalar.vector.im/api</string>
|
||||
</array>
|
||||
<key>piwik</key>
|
||||
|
|
1
Riot/Assets/apple_emojis_data.json
Normal file
1
Riot/Assets/apple_emojis_data.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -788,3 +788,35 @@
|
|||
// MARK: File upload
|
||||
"file_upload_error_title" = "Качване на файл";
|
||||
"file_upload_error_unsupported_file_type_message" = "Типът файл не се поддържа.";
|
||||
"auth_softlogout_signed_out" = "Излезли сте от профила";
|
||||
"auth_softlogout_sign_in" = "Влез";
|
||||
"auth_softlogout_reason" = "Администратора на вашия сървър (%1$@) ви е отписал от профила %2$@ (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "Влезте за да възстановите ключове за шифровани съхранени само на това устройство. Ще ви трябват за да можете да четете всички защитени съобщения на кое да е устройство.";
|
||||
"auth_softlogout_clear_data" = "Изчисти личните данни";
|
||||
"auth_softlogout_clear_data_message_1" = "Внимание: Личните ви данни (включително ключове за шифроване) все още са съхранени на това устройство.";
|
||||
"auth_softlogout_clear_data_message_2" = "Изчистете, ако сте приключили с използването на това устройство или искате да влезете с друг профил.";
|
||||
"auth_softlogout_clear_data_button" = "Изчисти всички данни";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Сигурни ли сте?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Сигурни ли сте, че искате да изчистите всички данни съхранени на това устройство? Влезте пак за да достъпите профила и съобщенията си.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Излез";
|
||||
"room_event_action_reaction_history" = "История на реакциите";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Реакции";
|
||||
"emoji_picker_people_category" = "Усмивки и хора";
|
||||
"emoji_picker_nature_category" = "Животни и природа";
|
||||
"emoji_picker_foods_category" = "Храна и напитки";
|
||||
"emoji_picker_activity_category" = "Дейности";
|
||||
"emoji_picker_places_category" = "Пътуване и места";
|
||||
"emoji_picker_objects_category" = "Обекти";
|
||||
"emoji_picker_symbols_category" = "Символи";
|
||||
"emoji_picker_flags_category" = "Флагове";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Реакции";
|
||||
"room_action_camera" = "Направи снимка или видео";
|
||||
// Media picker
|
||||
"media_picker_title" = "Медийна библиотека";
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Направи снимка";
|
||||
"image_picker_action_library" = "Избери от библиотеката";
|
||||
"camera_unavailable" = "Не е достъпна камера на вашето устройство";
|
||||
"photo_library_access_not_granted" = "%@ няма достъп до библиотеката със снимки. Моля, променете настройките на поверителността";
|
||||
|
|
|
@ -669,7 +669,7 @@
|
|||
"room_action_reply" = "Antworten";
|
||||
"settings_labs_message_reaction" = "Mit einem Emoji reagieren";
|
||||
"settings_key_backup_button_connect" = "Schlüssel dieses Geräts sichern";
|
||||
"event_formatter_message_edited_mention" = "(geändert)";
|
||||
"event_formatter_message_edited_mention" = "(bearbeitet)";
|
||||
"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Schlüssel dieses Geräts sichern";
|
||||
"key_backup_recover_connent_banner_subtitle" = "Schlüssel dieses Geräts sichern";
|
||||
// MARK: - Device Verification
|
||||
|
@ -736,3 +736,65 @@
|
|||
"device_verification_emoji_bell" = "Glocke";
|
||||
"device_verification_emoji_anchor" = "Anker";
|
||||
"device_verification_emoji_headphones" = "Kopfhörer";
|
||||
"close" = "Schließen";
|
||||
"auth_softlogout_signed_out" = "Sie sind abgemeldet";
|
||||
"auth_softlogout_sign_in" = "Einloggen";
|
||||
"auth_softlogout_reason" = "Ihr Homeserver-Administrator (%1$@) hat Sie von Ihrem Konto %2$@ (%3$@) abgemeldet.";
|
||||
"auth_softlogout_recover_encryption_keys" = "Melden Sie sich an, um Verschlüsselungsschlüssel wiederherzustellen, die ausschließlich auf diesem Gerät gespeichert sind. Sie benötigen sie, um alle Ihre sicheren Nachrichten auf jedem Gerät lesen zu können.";
|
||||
"auth_softlogout_clear_data" = "Persönliche Daten löschen";
|
||||
"auth_softlogout_clear_data_message_1" = "Warnung: Ihre persönlichen Daten (einschließlich Verschlüsselungsschlüssel) sind noch auf diesem Gerät gespeichert.";
|
||||
"auth_softlogout_clear_data_message_2" = "Deaktivieren Sie es, wenn Sie dieses Gerät nicht mehr verwenden oder sich bei einem anderen Konto anmelden möchten.";
|
||||
"auth_softlogout_clear_data_button" = "Lösche alle Daten";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Bist du sicher?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Möchten Sie wirklich alle derzeit auf diesem Gerät gespeicherten Daten löschen? Melden Sie sich erneut an, um auf Ihre Kontodaten und Nachrichten zuzugreifen.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Ausloggen";
|
||||
"room_event_action_reaction_show_all" = "Zeige alles";
|
||||
"room_event_action_reaction_show_less" = "Zeige weniger";
|
||||
"room_event_action_reaction_history" = "Reaktionsverlauf";
|
||||
"room_action_send_file" = "Datei senden";
|
||||
"room_message_edits_history_title" = "Bearbeitungsverlauf";
|
||||
// Widget
|
||||
"widget_no_integrations_server_configured" = "Kein Integrationsserver konfiguriert";
|
||||
"widget_integrations_server_failed_to_connect" = "Verbindung zum Integrationsserver fehlgeschlagen";
|
||||
"device_verification_security_advice" = "Für maximale Sicherheit empfehlen wir, dies persönlich zu tun oder ein anderes vertrauenswürdiges Kommunikationsmittel zu verwenden";
|
||||
"device_verification_incoming_description_1" = "Überprüfen Sie dieses Gerät, um es als vertrauenswürdig zu markieren. Das Vertrauen auf Geräte von Partnern gibt Ihnen zusätzliche Sicherheit, wenn Sie verschlüsselte End-to-End-Nachrichten verwenden.";
|
||||
"device_verification_incoming_description_2" = "Wenn Sie dieses Gerät überprüfen, wird es als vertrauenswürdig und für den Partner als vertrauenswürdig gekennzeichnet.";
|
||||
// MARK: Start
|
||||
"device_verification_start_title" = "Überprüfen Sie dies, indem Sie eine kurze Textzeichenfolge vergleichen";
|
||||
"device_verification_start_wait_partner" = "Warten auf Partner zu akzeptieren ...";
|
||||
"device_verification_start_use_legacy" = "Nichts auftauchend? Nicht alle Clients unterstützen die interaktive Überprüfung. Verwenden Sie die Alte-Überprüfung.";
|
||||
"device_verification_start_use_legacy_action" = "Verwenden Sie die Alte-Überprüfung";
|
||||
// MARK: Verify
|
||||
"device_verification_verify_title_emoji" = "Überprüfen Sie dieses Gerät, indem Sie bestätigen, dass das folgende Emoji auf dem Bildschirm des Partners angezeigt wird";
|
||||
"device_verification_verify_title_number" = "Überprüfen Sie dieses Gerät, indem Sie bestätigen, dass die folgenden Zahlen auf dem Bildschirm des Partners angezeigt werden";
|
||||
"device_verification_verify_wait_partner" = "Warten auf die Bestätigung des Partners...";
|
||||
// MARK: Verified
|
||||
"device_verification_verified_title" = "Verifiziert!";
|
||||
"device_verification_verified_description_1" = "Sie haben dieses Gerät erfolgreich überprüft.";
|
||||
"device_verification_verified_description_2" = "Verschlüsselte Nachrichten mit diesem Benutzer werden durchgehend verschlüsselt und können von Dritten nicht gelesen werden.";
|
||||
"device_verification_emoji_rooster" = "Hahn";
|
||||
"device_verification_emoji_globe" = "Globus";
|
||||
"device_verification_emoji_smiley" = "Lächeln";
|
||||
"device_verification_emoji_spanner" = "Spanner";
|
||||
"device_verification_emoji_thumbs up" = "Daumen hoch";
|
||||
"device_verification_emoji_hourglass" = "Sanduhr";
|
||||
"device_verification_emoji_clock" = "Uhr";
|
||||
"device_verification_emoji_pencil" = "Bleistift";
|
||||
"device_verification_emoji_lock" = "sperren";
|
||||
"device_verification_emoji_folder" = "Ordner";
|
||||
"device_verification_emoji_pin" = "Stift";
|
||||
// MARK: File upload
|
||||
"file_upload_error_title" = "Datei hochladen";
|
||||
"file_upload_error_unsupported_file_type_message" = "Dateityp wird nicht unterstützt.";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Reaktionen";
|
||||
"emoji_picker_people_category" = "Smileys & Menschen";
|
||||
"emoji_picker_nature_category" = "Tiere & Natur";
|
||||
"emoji_picker_foods_category" = "Essen und Trinken";
|
||||
"emoji_picker_activity_category" = "Aktivitäten";
|
||||
"emoji_picker_places_category" = "Reisen & Orte";
|
||||
"emoji_picker_objects_category" = "Objekte";
|
||||
"emoji_picker_symbols_category" = "Symbole";
|
||||
"emoji_picker_flags_category" = "Flaggen";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Reaktionen";
|
||||
|
|
|
@ -115,6 +115,18 @@
|
|||
"auth_add_email_and_phone_warning" = "Registration with email and phone number at once is not supported yet until the api exists. Only the phone number will be taken into account. You may add your email to your profile in settings.";
|
||||
"auth_accept_policies" = "Please review and accept the policies of this homeserver:";
|
||||
"auth_autodiscover_invalid_response" = "Invalid homeserver discovery response";
|
||||
"auth_softlogout_signed_out" = "You’re signed out";
|
||||
"auth_softlogout_sign_in" = "Sign In";
|
||||
"auth_softlogout_reason" = "Your homeserver (%1$@) admin has signed you out of your account %2$@ (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "Sign in to recover encryption keys stored exclusively on this device. You need them to read all of your secure messages on any device.";
|
||||
"auth_softlogout_clear_data" = "Clear personal data";
|
||||
"auth_softlogout_clear_data_message_1" = "Warning: Your personal data (including encryption keys) is still stored on this device.";
|
||||
"auth_softlogout_clear_data_message_2" = "Clear it if you're finished using this device, or want to sign in to another account.";
|
||||
"auth_softlogout_clear_data_button" = "Clear all data";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Are you sure?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Are you sure you want to clear all data currently stored on this device? Sign in again to access your account data and messages.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Sign out";
|
||||
|
||||
|
||||
// Chat creation
|
||||
"room_creation_title" = "New Chat";
|
||||
|
@ -283,8 +295,10 @@
|
|||
"room_event_action_edit" = "Edit";
|
||||
"room_event_action_reaction_show_all" = "Show all";
|
||||
"room_event_action_reaction_show_less" = "Show less";
|
||||
"room_event_action_reaction_history" = "Reaction history";
|
||||
"room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption.";
|
||||
"room_event_failed_to_send" = "Failed to send";
|
||||
"room_action_camera" = "Take photo or video";
|
||||
"room_action_send_photo_or_video" = "Send photo or video";
|
||||
"room_action_send_sticker" = "Send sticker";
|
||||
"room_action_send_file" = "Send file";
|
||||
|
@ -561,9 +575,14 @@
|
|||
"receipt_status_read" = "Read: ";
|
||||
|
||||
// Media picker
|
||||
"media_picker_title" = "Media library";
|
||||
"media_picker_library" = "Library";
|
||||
"media_picker_select" = "Select";
|
||||
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Take photo";
|
||||
"image_picker_action_library" = "Choose from library";
|
||||
|
||||
// Directory
|
||||
"directory_title" = "Directory";
|
||||
"directory_server_picker_title" = "Select a directory";
|
||||
|
@ -594,6 +613,8 @@
|
|||
"rage_shake_prompt" = "You seem to be shaking the phone in frustration. Would you like to submit a bug report?";
|
||||
"do_not_ask_again" = "Do not ask again";
|
||||
"camera_access_not_granted" = "%@ doesn't have permission to use Camera, please change privacy settings";
|
||||
"camera_unavailable" = "The camera is unavailable on your device";
|
||||
"photo_library_access_not_granted" = "%@ doesn't have permission to access photo library, please change privacy settings";
|
||||
"large_badge_value_k_format" = "%.1fK";
|
||||
"room_does_not_exist" = "%@ does not exist";
|
||||
|
||||
|
@ -904,3 +925,18 @@
|
|||
// MARK: File upload
|
||||
"file_upload_error_title" = "File upload";
|
||||
"file_upload_error_unsupported_file_type_message" = "File type not supported.";
|
||||
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Reactions";
|
||||
|
||||
"emoji_picker_people_category" = "Smileys & People";
|
||||
"emoji_picker_nature_category" = "Animals & Nature";
|
||||
"emoji_picker_foods_category" = "Food & Drink";
|
||||
"emoji_picker_activity_category" = "Activities";
|
||||
"emoji_picker_places_category" = "Travel & Places";
|
||||
"emoji_picker_objects_category" = "Objects";
|
||||
"emoji_picker_symbols_category" = "Symbols";
|
||||
"emoji_picker_flags_category" = "Flags";
|
||||
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Reactions";
|
||||
|
|
|
@ -740,7 +740,7 @@
|
|||
"device_verification_cancelled" = "Beste aldeak egiaztaketa ezeztatu du.";
|
||||
"device_verification_cancelled_by_me" = "Egiaztaketa ezeztatu da. Arrazoia: %@";
|
||||
"device_verification_emoji_hourglass" = "Harea-erlojua";
|
||||
"device_verification_emoji_clock" = "Klasea";
|
||||
"device_verification_emoji_clock" = "Erlojua";
|
||||
"device_verification_emoji_gift" = "Oparia";
|
||||
"device_verification_emoji_light bulb" = "Bonbilla";
|
||||
"device_verification_emoji_book" = "Liburua";
|
||||
|
@ -777,3 +777,24 @@
|
|||
// MARK: File upload
|
||||
"file_upload_error_title" = "Fitxategi igoera";
|
||||
"file_upload_error_unsupported_file_type_message" = "Fitxategi mota ez onartua.";
|
||||
"auth_softlogout_signed_out" = "Saioa amaitu duzu";
|
||||
"auth_softlogout_sign_in" = "Hasi saioa";
|
||||
"auth_softlogout_reason" = "Zure hasiera zerbitzariaren administratzaileak (%1$@) zure %2$@ kontuaren saioa amaitu du (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "hasi saioa gailu honetan besterik gorde ez diren zifratze gakoak berreskuratzeko. Zure mezu seguruak beste gailuetan irakurri ahal izateko behar dituzu.";
|
||||
"auth_softlogout_clear_data" = "Garbitu datu pertsonalak";
|
||||
"auth_softlogout_clear_data_message_1" = "Abisua: Zure datu pertsonalak (zure zifratze gakoak barne) gailu honetan gordeko dira.";
|
||||
"auth_softlogout_clear_data_message_2" = "Garbitu gailu honekin bukatu baduzu, edo beste saio bat hasi nahi baduzu.";
|
||||
"auth_softlogout_clear_data_button" = "Garbitu datu guztiak";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Ziur al zaude?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Ziur gailu honetan gordetako datu guztiak ezabatu nahi dituzula? Hasi saioa berriro zure kontuaren datuak eta mezuak atzitzeko.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Amaitu saioa";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Erreakzioak";
|
||||
"emoji_picker_people_category" = "Irribarretxoak eta jendea";
|
||||
"emoji_picker_nature_category" = "Animaliak eta natura";
|
||||
"emoji_picker_foods_category" = "Janaria eta edaria";
|
||||
"emoji_picker_activity_category" = "Jarduerak";
|
||||
"emoji_picker_places_category" = "Bidaiak eta tokiak";
|
||||
"emoji_picker_objects_category" = "Objektuak";
|
||||
"emoji_picker_symbols_category" = "Sinboloak";
|
||||
"emoji_picker_flags_category" = "Banderak";
|
||||
|
|
|
@ -789,3 +789,35 @@
|
|||
// MARK: File upload
|
||||
"file_upload_error_title" = "Envoi de fichier";
|
||||
"file_upload_error_unsupported_file_type_message" = "Type de fichier non pris en charge.";
|
||||
"auth_softlogout_signed_out" = "Vous êtes déconnecté(e)";
|
||||
"auth_softlogout_sign_in" = "Se connecter";
|
||||
"auth_softlogout_reason" = "L’administrateur de votre serveur d’accueil (%1$@) vous a déconnecté de votre compte %2$@ (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "Connectez-vous pour récupérer les clés de chiffrement stockées uniquement sur cet appareil. Vous en avez besoin pour lire tous les messages sécurisés sur n’importe quel appareil.";
|
||||
"auth_softlogout_clear_data" = "Effacer les données personnelles";
|
||||
"auth_softlogout_clear_data_message_1" = "Attention : Vos données personnelles (y compris vos clés de chiffrement) sont toujours stockées sur cet appareil.";
|
||||
"auth_softlogout_clear_data_message_2" = "Effacez-les si vous n’utilisez plus cet appareil ou si vous voulez vous connecter avec un autre compte.";
|
||||
"auth_softlogout_clear_data_button" = "Effacer toutes les données";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "En êtes-vous sûr(e) ?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Voulez vous vraiment supprimer toutes les données stockées actuellement sur cet appareil ? Reconnectez-vous pour accéder aux données et messages de votre compte.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Se déconnecter";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Réactions";
|
||||
"emoji_picker_people_category" = "Émoticônes et personnes";
|
||||
"emoji_picker_nature_category" = "Animaux et nature";
|
||||
"emoji_picker_foods_category" = "Nourriture et boisson";
|
||||
"emoji_picker_activity_category" = "Activités";
|
||||
"emoji_picker_places_category" = "Voyage et lieux";
|
||||
"emoji_picker_objects_category" = "Objets";
|
||||
"emoji_picker_symbols_category" = "Symboles";
|
||||
"emoji_picker_flags_category" = "Drapeaux";
|
||||
"room_event_action_reaction_history" = "Historique des réactions";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Réactions";
|
||||
"room_action_camera" = "Prendre une photo ou une vidéo";
|
||||
// Media picker
|
||||
"media_picker_title" = "Médiathèque";
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Prendre une photo";
|
||||
"image_picker_action_library" = "Choisir dans la médiathèque";
|
||||
"camera_unavailable" = "L’appareil photo n’est pas disponible sur votre appareil";
|
||||
"photo_library_access_not_granted" = "%@ n’a pas la permission pour accéder à la médiathèque, veuillez modifier les options de vie privée";
|
||||
|
|
|
@ -756,7 +756,7 @@
|
|||
"device_verification_emoji_thumbs up" = "Hüvelykujj fel";
|
||||
"device_verification_emoji_umbrella" = "Esernyő";
|
||||
"device_verification_emoji_hourglass" = "Homokóra";
|
||||
"device_verification_emoji_clock" = "Osztály";
|
||||
"device_verification_emoji_clock" = "Óra";
|
||||
"device_verification_emoji_gift" = "Ajándék";
|
||||
"device_verification_emoji_light bulb" = "Égő";
|
||||
"device_verification_emoji_book" = "Könyv";
|
||||
|
@ -794,3 +794,27 @@
|
|||
// MARK: File upload
|
||||
"file_upload_error_title" = "Fájl feltöltés";
|
||||
"file_upload_error_unsupported_file_type_message" = "A fájl típusa nem támogatott.";
|
||||
"auth_softlogout_signed_out" = "Kijelentkeztél";
|
||||
"auth_softlogout_sign_in" = "Bejelentkezés";
|
||||
"auth_softlogout_reason" = "A matrix szerver (%1$@) adminisztrátora kiléptetett a felhasználói fiókodból %2$@ (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "A csak ezen az eszközön meglévő titkosítási kulcsokhoz való hozzáféréshez be kell jelentkezned. Ahhoz hogy bármelyik eszközön elolvashasd a titkosított üzeneteidet szükséged lesz rájuk.";
|
||||
"auth_softlogout_clear_data" = "Személyes adatok törlése";
|
||||
"auth_softlogout_clear_data_message_1" = "Figyelmeztetés: A személyes adataid (beleértve a titkosítási kulcsokat) továbbra is az eszközön tárolódnak.";
|
||||
"auth_softlogout_clear_data_message_2" = "Ha már nem akarod használni ezt az eszközt vagy másik fiókba szeretnél bejelentkezni akkor töröld őket.";
|
||||
"auth_softlogout_clear_data_button" = "Minden adat törlése";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Biztos vagy benne?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Biztos vagy benne, hogy minden az eszközön tárolt adatot törölni szeretnél? A fiók és az üzeneteid eléréséhez jelentkezz be.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Kilépés";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Reakciók";
|
||||
"emoji_picker_people_category" = "Smiley-k és emberek";
|
||||
"emoji_picker_nature_category" = "Állatok és természet";
|
||||
"emoji_picker_foods_category" = "Étel és ital";
|
||||
"emoji_picker_activity_category" = "Mozgás";
|
||||
"emoji_picker_places_category" = "Utazás és helyek";
|
||||
"emoji_picker_objects_category" = "Tárgyak";
|
||||
"emoji_picker_symbols_category" = "Szimbólumok";
|
||||
"emoji_picker_flags_category" = "Zászlók";
|
||||
"room_event_action_reaction_history" = "Reakciók története";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Reakciók";
|
||||
|
|
|
@ -784,8 +784,48 @@
|
|||
"device_verification_emoji_headphones" = "Koptelefoon";
|
||||
"device_verification_emoji_folder" = "Map";
|
||||
"device_verification_emoji_pin" = "Speld";
|
||||
"event_formatter_message_edited_mention" = "(Bewerkt)";
|
||||
"event_formatter_message_edited_mention" = "(bewerkt)";
|
||||
// Widget
|
||||
"widget_no_integrations_server_configured" = "Geen integratieserver geconfigureerd";
|
||||
"widget_integrations_server_failed_to_connect" = "Verbinden met integratieserver mislukt";
|
||||
"device_verification_emoji_lock" = "Slot";
|
||||
"close" = "Sluiten";
|
||||
"auth_softlogout_signed_out" = "U bent afgemeld";
|
||||
"auth_softlogout_sign_in" = "Aanmelden";
|
||||
"auth_softlogout_reason" = "De beheerder van uw thuisserver (%1$@) heeft u van uw account %2$@ afgemeld (%3$@).";
|
||||
"auth_softlogout_recover_encryption_keys" = "Meld u aan om de versleutelingssleutels te herstellen die uitsluitend op dit apparaat worden opgeslagen. U heeft ze nodig om uw versleutelde berichten op al uw apparaten te kunnen lezen.";
|
||||
"auth_softlogout_clear_data" = "Persoonlijke gegevens wissen";
|
||||
"auth_softlogout_clear_data_message_1" = "Let op: uw persoonlijke gegevens (inclusief versleutelingssleutels) worden nog altijd op dit apparaat opgeslagen.";
|
||||
"auth_softlogout_clear_data_message_2" = "Wis ze indien u dit apparaat niet meer gebruikt, of indien u zich wilt aanmelden met een andere account.";
|
||||
"auth_softlogout_clear_data_button" = "Alle gegevens wissen";
|
||||
"auth_softlogout_clear_data_sign_out_title" = "Weet u het zeker?";
|
||||
"auth_softlogout_clear_data_sign_out_msg" = "Weet u zeker dat u alle gegevens die op dit moment op dit apparaat worden opgeslagen wilt wissen? Meld u opnieuw aan om toegang te verkrijgen tot uw accountgegevens en berichten.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Afmelden";
|
||||
"room_event_action_reaction_show_all" = "Alles tonen";
|
||||
"room_event_action_reaction_show_less" = "Minder tonen";
|
||||
"room_event_action_reaction_history" = "Reactiegeschiedenis";
|
||||
"room_action_camera" = "Foto of video maken";
|
||||
"room_action_send_file" = "Bestand versturen";
|
||||
"room_message_edits_history_title" = "Berichtbewerkingen";
|
||||
// Media picker
|
||||
"media_picker_title" = "Mediatheek";
|
||||
// Image picker
|
||||
"image_picker_action_camera" = "Foto maken";
|
||||
"image_picker_action_library" = "Kiezen uit mediatheek";
|
||||
"camera_unavailable" = "De camera is niet beschikbaar op uw apparaat";
|
||||
"photo_library_access_not_granted" = "%@ heeft geen toegang tot de fotobibliotheek, wijzig uw privacy-instellingen";
|
||||
// MARK: File upload
|
||||
"file_upload_error_title" = "Bestand uploaden";
|
||||
"file_upload_error_unsupported_file_type_message" = "Bestandstype niet ondersteund.";
|
||||
// MARK: Emoji picker
|
||||
"emoji_picker_title" = "Reacties";
|
||||
"emoji_picker_people_category" = "Smileys en personen";
|
||||
"emoji_picker_nature_category" = "Dieren en natuur";
|
||||
"emoji_picker_foods_category" = "Eten en drinken";
|
||||
"emoji_picker_activity_category" = "Activiteiten";
|
||||
"emoji_picker_places_category" = "Reizen en plaatsen";
|
||||
"emoji_picker_objects_category" = "Voorwerpen";
|
||||
"emoji_picker_symbols_category" = "Symbolen";
|
||||
"emoji_picker_flags_category" = "Vlaggen";
|
||||
// MARK: Reaction history
|
||||
"reaction_history_title" = "Reacties";
|
||||
|
|
|
@ -510,3 +510,19 @@
|
|||
"settings_on_denied_notification" = "Powiadomienia są odrzucane w %@, proszę zezwól na nie w ustawieniach urządzenia";
|
||||
"settings_callkit_info" = "Odbieraj połączenia przychodzące na ekranie blokady. Zobacz swoje połęczenia Riot w historii połączeń w systemie. Jeśli usługa iCloud jest włączona, historia połączeń zostanie udostępniona Apple.";
|
||||
"settings_ui_theme_picker_message" = "\"Auto\" używa ustawienia \"Odwróć kolory\" urządzenia";
|
||||
"close" = "Zamknij";
|
||||
"auth_softlogout_sign_in" = "Zaloguj się";
|
||||
"auth_softlogout_clear_data_button" = "Wyczyść wszystkie dane";
|
||||
"auth_softlogout_clear_data_sign_out" = "Wyloguj się";
|
||||
"room_event_action_reaction_show_all" = "Pokaż wszystko";
|
||||
"room_event_action_reaction_show_less" = "Pokaż mniej";
|
||||
"room_action_send_file" = "Wyślij plik";
|
||||
"room_message_edits_history_title" = "Edycje wiadomości";
|
||||
"settings_enable_callkit" = "Zintegrowane połączenie";
|
||||
"settings_key_backup_info_checking" = "Sprawdzanie...";
|
||||
"settings_key_backup_info_version" = "Wersja kopii zapasowej kluczy: %@";
|
||||
"settings_key_backup_info_algorithm" = "Algorytm: %@";
|
||||
"settings_key_backup_info_progress" = "Tworzenie kopii zapasowej %@ kluczy...";
|
||||
"settings_key_backup_info_progress_done" = "Utworzono kopię zapasową wszystkich kluczy";
|
||||
"settings_key_backup_button_delete" = "Usuń kopię zapasową";
|
||||
"settings_key_backup_delete_confirmation_prompt_title" = "Usuń kopię zapasową";
|
||||
|
|
|
@ -1359,6 +1359,23 @@
|
|||
href="https://www.apache.org/licenses/LICENSE-2.0">https://www.apache.org/licenses/LICENSE-2.0</a>
|
||||
<br/><br/>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.
|
||||
</li>
|
||||
<li>
|
||||
<p><b>Emoji Mart</b> (<a href="https://github.com/missive/emoji-mart">https://github.com/missive/emoji-mart</a>)</p>
|
||||
<pre>
|
||||
Copyright (c) 2016, Missive
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</pre>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1 +1,12 @@
|
|||
|
||||
// String for App Store
|
||||
"store_short_description" = "Veilig en gedecentraliseerd chattn en belln";
|
||||
// Titles
|
||||
"title_home" = "Thuus";
|
||||
"title_favourites" = "Favorietn";
|
||||
"title_people" = "Menschn";
|
||||
"title_rooms" = "Gesprekkn";
|
||||
"title_groups" = "Gemeenschappn";
|
||||
"warning" = "Woarschuwienge";
|
||||
// Actions
|
||||
"view" = "Toogn";
|
||||
"next" = "Volgende";
|
||||
|
|
|
@ -26,10 +26,22 @@ extern NSString *const kMXKRoomBubbleCellRiotEditButtonPressed;
|
|||
/**
|
||||
Action identifier used when the user tapped on receipts area.
|
||||
|
||||
The 'userInfo' disctionary contains an 'MXKReceiptSendersContainer' object under the 'kMXKRoomBubbleCellReceiptsContainerKey' key, representing the receipts container which was tapped on.
|
||||
The 'userInfo' dictionary contains an 'MXKReceiptSendersContainer' object under the 'kMXKRoomBubbleCellReceiptsContainerKey' key, representing the receipts container which was tapped on.
|
||||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer;
|
||||
|
||||
/**
|
||||
Action identifier used when the user perform a long press on reactions view.
|
||||
|
||||
The 'userInfo' dictionary contains a 'NSString' object under the 'kMXKRoomBubbleCellEventIdKey' key, representing the event id of the event associated with the reactions.
|
||||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellLongPressOnReactionView;
|
||||
|
||||
/**
|
||||
'userInfo' dictionary key 'kMXKRoomBubbleCellEventIdKey' is associated to a 'NSString' object representing an event id.
|
||||
*/
|
||||
extern NSString *const kMXKRoomBubbleCellEventIdKey;
|
||||
|
||||
/**
|
||||
Define a `MXKRoomBubbleTableViewCell` category at Riot level to handle bubble customisation.
|
||||
*/
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
NSString *const kMXKRoomBubbleCellRiotEditButtonPressed = @"kMXKRoomBubbleCellRiotEditButtonPressed";
|
||||
NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellTapOnReceiptsContainer";
|
||||
NSString *const kMXKRoomBubbleCellLongPressOnReactionView = @"kMXKRoomBubbleCellLongPressOnReactionView";
|
||||
NSString *const kMXKRoomBubbleCellEventIdKey = @"kMXKRoomBubbleCellEventIdKey";
|
||||
|
||||
@implementation MXKRoomBubbleTableViewCell (Riot)
|
||||
|
||||
|
|
|
@ -29,4 +29,13 @@ extension String {
|
|||
}
|
||||
return abs(hash)
|
||||
}
|
||||
|
||||
/// Locale-independent case-insensitive contains
|
||||
/// Note: Prefer use `localizedCaseInsensitiveContains` when locale matters
|
||||
///
|
||||
/// - Parameter other: The other string.
|
||||
/// - Returns: true if current string contains other string.
|
||||
func vc_caseInsensitiveContains(_ other: String) -> Bool {
|
||||
return self.range(of: other, options: .caseInsensitive) != nil
|
||||
}
|
||||
}
|
||||
|
|
34
Riot/Categories/UISearchBar.swift
Normal file
34
Riot/Categories/UISearchBar.swift
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UISearchBar {
|
||||
|
||||
/// Returns internal UITextField
|
||||
@objc var vc_searchTextField: UITextField? {
|
||||
// TODO: To remove once on XCode11/iOS13
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 13.0, *) {
|
||||
return self.searchTextField
|
||||
} else {
|
||||
return self.value(forKey: "searchField") as? UITextField
|
||||
}
|
||||
#else
|
||||
return self.value(forKey: "searchField") as? UITextField
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -89,6 +89,7 @@ internal enum Asset {
|
|||
internal static let mainAliasIcon = ImageAsset(name: "main_alias_icon")
|
||||
internal static let membersListIcon = ImageAsset(name: "members_list_icon")
|
||||
internal static let modIcon = ImageAsset(name: "mod_icon")
|
||||
internal static let moreReactions = ImageAsset(name: "more_reactions")
|
||||
internal static let fileDocIcon = ImageAsset(name: "file_doc_icon")
|
||||
internal static let fileMusicIcon = ImageAsset(name: "file_music_icon")
|
||||
internal static let filePhotoIcon = ImageAsset(name: "file_photo_icon")
|
||||
|
|
|
@ -42,6 +42,11 @@ internal enum StoryboardScene {
|
|||
|
||||
internal static let initialScene = InitialSceneType<Riot.EditHistoryViewController>(storyboard: EditHistoryViewController.self)
|
||||
}
|
||||
internal enum EmojiPickerViewController: StoryboardType {
|
||||
internal static let storyboardName = "EmojiPickerViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.EmojiPickerViewController>(storyboard: EmojiPickerViewController.self)
|
||||
}
|
||||
internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType {
|
||||
internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController"
|
||||
|
||||
|
@ -77,6 +82,11 @@ internal enum StoryboardScene {
|
|||
|
||||
internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromRecoveryKeyViewController>(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self)
|
||||
}
|
||||
internal enum ReactionHistoryViewController: StoryboardType {
|
||||
internal static let storyboardName = "ReactionHistoryViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.ReactionHistoryViewController>(storyboard: ReactionHistoryViewController.self)
|
||||
}
|
||||
internal enum RoomContextualMenuViewController: StoryboardType {
|
||||
internal static let storyboardName = "RoomContextualMenuViewController"
|
||||
|
||||
|
|
|
@ -226,6 +226,50 @@ internal enum VectorL10n {
|
|||
internal static var authSkip: String {
|
||||
return VectorL10n.tr("Vector", "auth_skip")
|
||||
}
|
||||
/// Clear personal data
|
||||
internal static var authSoftlogoutClearData: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data")
|
||||
}
|
||||
/// Clear all data
|
||||
internal static var authSoftlogoutClearDataButton: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_button")
|
||||
}
|
||||
/// Warning: Your personal data (including encryption keys) is still stored on this device.
|
||||
internal static var authSoftlogoutClearDataMessage1: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_message_1")
|
||||
}
|
||||
/// Clear it if you're finished using this device, or want to sign in to another account.
|
||||
internal static var authSoftlogoutClearDataMessage2: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_message_2")
|
||||
}
|
||||
/// Sign out
|
||||
internal static var authSoftlogoutClearDataSignOut: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_sign_out")
|
||||
}
|
||||
/// Are you sure you want to clear all data currently stored on this device? Sign in again to access your account data and messages.
|
||||
internal static var authSoftlogoutClearDataSignOutMsg: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_sign_out_msg")
|
||||
}
|
||||
/// Are you sure?
|
||||
internal static var authSoftlogoutClearDataSignOutTitle: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_clear_data_sign_out_title")
|
||||
}
|
||||
/// Your homeserver (%1$@) admin has signed you out of your account %2$@ (%3$@).
|
||||
internal static func authSoftlogoutReason(_ p1: String, _ p2: String, _ p3: String) -> String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_reason", p1, p2, p3)
|
||||
}
|
||||
/// Sign in to recover encryption keys stored exclusively on this device. You need them to read all of your secure messages on any device.
|
||||
internal static var authSoftlogoutRecoverEncryptionKeys: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_recover_encryption_keys")
|
||||
}
|
||||
/// Sign In
|
||||
internal static var authSoftlogoutSignIn: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_sign_in")
|
||||
}
|
||||
/// You’re signed out
|
||||
internal static var authSoftlogoutSignedOut: String {
|
||||
return VectorL10n.tr("Vector", "auth_softlogout_signed_out")
|
||||
}
|
||||
/// Submit
|
||||
internal static var authSubmit: String {
|
||||
return VectorL10n.tr("Vector", "auth_submit")
|
||||
|
@ -330,6 +374,10 @@ internal enum VectorL10n {
|
|||
internal static func cameraAccessNotGranted(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "camera_access_not_granted", p1)
|
||||
}
|
||||
/// The camera is unavailable on your device
|
||||
internal static var cameraUnavailable: String {
|
||||
return VectorL10n.tr("Vector", "camera_unavailable")
|
||||
}
|
||||
/// Cancel
|
||||
internal static var cancel: String {
|
||||
return VectorL10n.tr("Vector", "cancel")
|
||||
|
@ -870,6 +918,42 @@ internal enum VectorL10n {
|
|||
internal static var e2eRoomKeyRequestTitle: String {
|
||||
return VectorL10n.tr("Vector", "e2e_room_key_request_title")
|
||||
}
|
||||
/// Activities
|
||||
internal static var emojiPickerActivityCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_activity_category")
|
||||
}
|
||||
/// Flags
|
||||
internal static var emojiPickerFlagsCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_flags_category")
|
||||
}
|
||||
/// Food & Drink
|
||||
internal static var emojiPickerFoodsCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_foods_category")
|
||||
}
|
||||
/// Animals & Nature
|
||||
internal static var emojiPickerNatureCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_nature_category")
|
||||
}
|
||||
/// Objects
|
||||
internal static var emojiPickerObjectsCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_objects_category")
|
||||
}
|
||||
/// Smileys & People
|
||||
internal static var emojiPickerPeopleCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_people_category")
|
||||
}
|
||||
/// Travel & Places
|
||||
internal static var emojiPickerPlacesCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_places_category")
|
||||
}
|
||||
/// Symbols
|
||||
internal static var emojiPickerSymbolsCategory: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_symbols_category")
|
||||
}
|
||||
/// Reactions
|
||||
internal static var emojiPickerTitle: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_title")
|
||||
}
|
||||
/// Send an encrypted message…
|
||||
internal static var encryptedRoomMessagePlaceholder: String {
|
||||
return VectorL10n.tr("Vector", "encrypted_room_message_placeholder")
|
||||
|
@ -1030,6 +1114,14 @@ internal enum VectorL10n {
|
|||
internal static var homeserverConnectionLost: String {
|
||||
return VectorL10n.tr("Vector", "homeserver_connection_lost")
|
||||
}
|
||||
/// Take photo
|
||||
internal static var imagePickerActionCamera: String {
|
||||
return VectorL10n.tr("Vector", "image_picker_action_camera")
|
||||
}
|
||||
/// Choose from library
|
||||
internal static var imagePickerActionLibrary: String {
|
||||
return VectorL10n.tr("Vector", "image_picker_action_library")
|
||||
}
|
||||
/// Invite
|
||||
internal static var invite: String {
|
||||
return VectorL10n.tr("Vector", "invite")
|
||||
|
@ -1274,6 +1366,10 @@ internal enum VectorL10n {
|
|||
internal static var mediaPickerSelect: String {
|
||||
return VectorL10n.tr("Vector", "media_picker_select")
|
||||
}
|
||||
/// Media library
|
||||
internal static var mediaPickerTitle: String {
|
||||
return VectorL10n.tr("Vector", "media_picker_title")
|
||||
}
|
||||
/// The Internet connection appears to be offline.
|
||||
internal static var networkOfflinePrompt: String {
|
||||
return VectorL10n.tr("Vector", "network_offline_prompt")
|
||||
|
@ -1314,6 +1410,10 @@ internal enum VectorL10n {
|
|||
internal static var peopleNoConversation: String {
|
||||
return VectorL10n.tr("Vector", "people_no_conversation")
|
||||
}
|
||||
/// %@ doesn't have permission to access photo library, please change privacy settings
|
||||
internal static func photoLibraryAccessNotGranted(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "photo_library_access_not_granted", p1)
|
||||
}
|
||||
/// Preview
|
||||
internal static var preview: String {
|
||||
return VectorL10n.tr("Vector", "preview")
|
||||
|
@ -1326,6 +1426,10 @@ internal enum VectorL10n {
|
|||
internal static var rageShakePrompt: String {
|
||||
return VectorL10n.tr("Vector", "rage_shake_prompt")
|
||||
}
|
||||
/// Reactions
|
||||
internal static var reactionHistoryTitle: String {
|
||||
return VectorL10n.tr("Vector", "reaction_history_title")
|
||||
}
|
||||
/// Read Receipts List
|
||||
internal static var readReceiptsList: String {
|
||||
return VectorL10n.tr("Vector", "read_receipts_list")
|
||||
|
@ -1354,6 +1458,10 @@ internal enum VectorL10n {
|
|||
internal static var retry: String {
|
||||
return VectorL10n.tr("Vector", "retry")
|
||||
}
|
||||
/// Take photo or video
|
||||
internal static var roomActionCamera: String {
|
||||
return VectorL10n.tr("Vector", "room_action_camera")
|
||||
}
|
||||
/// Reply
|
||||
internal static var roomActionReply: String {
|
||||
return VectorL10n.tr("Vector", "room_action_reply")
|
||||
|
@ -1738,6 +1846,10 @@ internal enum VectorL10n {
|
|||
internal static var roomEventActionQuote: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_quote")
|
||||
}
|
||||
/// Reaction history
|
||||
internal static var roomEventActionReactionHistory: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_reaction_history")
|
||||
}
|
||||
/// Show all
|
||||
internal static var roomEventActionReactionShowAll: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_reaction_show_all")
|
||||
|
|
35
Riot/Managers/Serialization/SerializationService.swift
Normal file
35
Riot/Managers/Serialization/SerializationService.swift
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
final class SerializationService: SerializationServiceType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let decoder = JSONDecoder()
|
||||
private let encoder = JSONEncoder()
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func deserialize<T: Decodable>(_ data: Data) throws -> T {
|
||||
return try decoder.decode(T.self, from: data)
|
||||
}
|
||||
|
||||
func serialize<T: Encodable>(_ object: T) throws -> Data {
|
||||
return try encoder.encode(object)
|
||||
}
|
||||
}
|
22
Riot/Managers/Serialization/SerializationServiceType.swift
Normal file
22
Riot/Managers/Serialization/SerializationServiceType.swift
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SerializationServiceType {
|
||||
func deserialize<T: Decodable>(_ data: Data) throws -> T
|
||||
func serialize<T: Encodable>(_ object: T) throws -> Data
|
||||
}
|
|
@ -93,7 +93,11 @@ class DarkTheme: NSObject, Theme {
|
|||
func applyStyle(onSearchBar searchBar: UISearchBar) {
|
||||
searchBar.barStyle = .black
|
||||
searchBar.tintColor = self.searchPlaceholderColor
|
||||
searchBar.barTintColor = self.headerBackgroundColor
|
||||
searchBar.barTintColor = self.headerBackgroundColor
|
||||
|
||||
if let searchBarTextField = searchBar.vc_searchTextField {
|
||||
searchBarTextField.textColor = searchBar.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
func applyStyle(onTextField texField: UITextField) {
|
||||
|
|
|
@ -94,6 +94,10 @@ class DefaultTheme: NSObject, Theme {
|
|||
searchBar.barStyle = .default
|
||||
searchBar.tintColor = self.searchPlaceholderColor
|
||||
searchBar.barTintColor = self.headerBackgroundColor
|
||||
|
||||
if let searchBarTextField = searchBar.vc_searchTextField {
|
||||
searchBarTextField.textColor = searchBar.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
func applyStyle(onTextField texField: UITextField) {
|
||||
|
|
|
@ -41,5 +41,9 @@
|
|||
@property (weak, nonatomic) IBOutlet UIView *homeServerSeparator;
|
||||
@property (weak, nonatomic) IBOutlet UIView *identityServerSeparator;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *softLogoutClearDataContainer;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *softLogoutClearDataLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *softLogoutClearDataButton;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
@ -107,6 +108,14 @@
|
|||
[self.customServersTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateHighlighted];
|
||||
|
||||
[self hideCustomServers:YES];
|
||||
|
||||
// Soft logout section
|
||||
self.softLogoutClearDataButton.layer.cornerRadius = 5;
|
||||
self.softLogoutClearDataButton.clipsToBounds = YES;
|
||||
[self.softLogoutClearDataButton setTitle:NSLocalizedStringFromTable(@"auth_softlogout_clear_data_button", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.softLogoutClearDataButton setTitle:NSLocalizedStringFromTable(@"auth_softlogout_clear_data_button", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
self.softLogoutClearDataButton.enabled = YES;
|
||||
self.softLogoutClearDataContainer.hidden = YES;
|
||||
|
||||
// The view controller dismiss itself on successful login.
|
||||
self.delegate = self;
|
||||
|
@ -194,7 +203,10 @@
|
|||
self.identityServerLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
|
||||
self.softLogoutClearDataLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.softLogoutClearDataButton.backgroundColor = ThemeService.shared.theme.warningColor;
|
||||
|
||||
[self.authInputsView customizeViewRendering];
|
||||
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
|
@ -288,6 +300,7 @@
|
|||
}
|
||||
|
||||
[self updateForgotPwdButtonVisibility];
|
||||
[self updateSoftLogoutClearDataContainerVisibility];
|
||||
}
|
||||
|
||||
- (void)setAuthInputsView:(MXKAuthInputsView *)authInputsView
|
||||
|
@ -366,7 +379,7 @@
|
|||
// The right bar button is used to switch the authentication type.
|
||||
if (self.authType == MXKAuthenticationTypeLogin)
|
||||
{
|
||||
if (!authInputsview.isSingleSignOnRequired)
|
||||
if (!authInputsview.isSingleSignOnRequired && !self.softLogoutCredentials)
|
||||
{
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"auth_register", @"Vector", nil);
|
||||
}
|
||||
|
@ -395,19 +408,121 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setSoftLogoutCredentials:(MXCredentials *)softLogoutCredentials
|
||||
{
|
||||
[super setSoftLogoutCredentials:softLogoutCredentials];
|
||||
|
||||
// Customise the screen for soft logout
|
||||
self.customServersTickButton.hidden = YES;
|
||||
self.rightBarButtonItem.title = nil;
|
||||
self.mainNavigationItem.title = NSLocalizedStringFromTable(@"auth_softlogout_signed_out", @"Vector", nil);
|
||||
|
||||
[self showSoftLogoutClearDataContainer];
|
||||
}
|
||||
|
||||
- (void)showSoftLogoutClearDataContainer
|
||||
{
|
||||
NSMutableAttributedString *message = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_softlogout_clear_data", @"Vector", nil)
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]
|
||||
}];
|
||||
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];
|
||||
|
||||
NSString *string = [NSString stringWithFormat:@"%@\n\n%@",
|
||||
NSLocalizedStringFromTable(@"auth_softlogout_clear_data_message_1", @"Vector", nil),
|
||||
NSLocalizedStringFromTable(@"auth_softlogout_clear_data_message_2", @"Vector", nil)];
|
||||
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:string
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
||||
}]];
|
||||
self.softLogoutClearDataLabel.attributedText = message;
|
||||
|
||||
self.softLogoutClearDataContainer.hidden = NO;
|
||||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
||||
- (void)updateSoftLogoutClearDataContainerVisibility
|
||||
{
|
||||
// Do not display it in case of forget password flow
|
||||
if (self.softLogoutCredentials && self.authType == MXKAuthenticationTypeLogin)
|
||||
{
|
||||
self.softLogoutClearDataContainer.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.softLogoutClearDataContainer.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showClearDataAfterSoftLogoutConfirmation
|
||||
{
|
||||
// Request confirmation
|
||||
if (alert)
|
||||
{
|
||||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"auth_softlogout_clear_data_sign_out_title", @"Vector", nil)
|
||||
message:NSLocalizedStringFromTable(@"auth_softlogout_clear_data_sign_out_msg", @"Vector", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"auth_softlogout_clear_data_sign_out", @"Vector", nil) style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
[self clearDataAfterSoftLogout];
|
||||
}]];
|
||||
|
||||
MXWeakify(self);
|
||||
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action)
|
||||
{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->alert = nil;
|
||||
}]];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)clearDataAfterSoftLogout
|
||||
{
|
||||
NSLog(@"[AuthenticationVC] clearDataAfterSoftLogout %@", self.softLogoutCredentials.userId);
|
||||
|
||||
// Use AppDelegate so that we reset app settings and this auth screen
|
||||
[[AppDelegate theDelegate] logoutSendingRequestServer:YES completion:^(BOOL isLoggedOut) {
|
||||
NSLog(@"[AuthenticationVC] Complete. isLoggedOut: %@", @(isLoggedOut));
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession
|
||||
{
|
||||
[super handleAuthenticationSession:authSession];
|
||||
|
||||
// Hide "Forgot password" and "Log in" buttons in case of SSO
|
||||
[self updateForgotPwdButtonVisibility];
|
||||
|
||||
AuthInputsView *authInputsview;
|
||||
if ([self.authInputsView isKindOfClass:AuthInputsView.class])
|
||||
{
|
||||
authInputsview = (AuthInputsView*)self.authInputsView;
|
||||
}
|
||||
|
||||
// Hide "Forgot password" and "Log in" buttons in case of SSO
|
||||
[self updateForgotPwdButtonVisibility];
|
||||
[self updateSoftLogoutClearDataContainerVisibility];
|
||||
|
||||
self.submitButton.hidden = authInputsview.isSingleSignOnRequired;
|
||||
|
||||
// Bind ssoButton again if self.authInputsView has changed
|
||||
[authInputsview.ssoButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
if (authInputsview.isSingleSignOnRequired && self.softLogoutCredentials)
|
||||
{
|
||||
// Remove submitButton so that the 2nd contraint on softLogoutClearDataContainer.top will be applied
|
||||
// That makes softLogoutClearDataContainer appear upper in the screen
|
||||
[self.submitButton removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
|
@ -529,10 +644,16 @@
|
|||
|
||||
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
||||
}
|
||||
else if (sender == self.softLogoutClearDataButton)
|
||||
{
|
||||
[self showClearDataAfterSoftLogoutConfirmation];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super onButtonPressed:sender];
|
||||
}
|
||||
|
||||
[self updateSoftLogoutClearDataContainerVisibility];
|
||||
}
|
||||
|
||||
- (void)onFailureDuringAuthRequest:(NSError *)error
|
||||
|
@ -540,7 +661,7 @@
|
|||
// Homeserver migration: When the default homeserver url is different from matrix.org,
|
||||
// the login (or forgot pwd) process with an existing matrix.org accounts will then fail.
|
||||
// Patch: Falling back to matrix.org HS so we don't break everyone's logins
|
||||
if ([self.homeServerTextField.text isEqualToString:self.defaultHomeServerUrl] && ![self.defaultHomeServerUrl isEqualToString:@"https://matrix.org"])
|
||||
if ([self.homeServerTextField.text isEqualToString:self.defaultHomeServerUrl] && ![self.defaultHomeServerUrl isEqualToString:@"https://matrix.org"] && !self.softLogoutCredentials)
|
||||
{
|
||||
MXError *mxError = [[MXError alloc] initWithNSError:error];
|
||||
|
||||
|
@ -710,7 +831,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!self.softLogoutClearDataContainer.isHidden)
|
||||
{
|
||||
// The soft logout clear data section adds more height
|
||||
constant += self.softLogoutClearDataContainer.frame.size.height;
|
||||
}
|
||||
|
||||
self.contentViewHeightConstraint.constant = constant;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina5_5" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -42,6 +42,9 @@
|
|||
<outlet property="rightBarButtonItem" destination="Kwt-KN-aVL" id="Y3F-wA-tf8"/>
|
||||
<outlet property="serverOptionsContainer" destination="FIn-2w-e6H" id="Z0e-NN-3LY"/>
|
||||
<outlet property="skipButton" destination="wEJ-AF-rdH" id="smu-MS-2IY"/>
|
||||
<outlet property="softLogoutClearDataButton" destination="ZRO-C2-hH1" id="PDa-aM-Hso"/>
|
||||
<outlet property="softLogoutClearDataContainer" destination="vX2-5Y-rQc" id="mgK-41-cnX"/>
|
||||
<outlet property="softLogoutClearDataLabel" destination="QYL-Lo-tmH" id="ks9-5X-xfs"/>
|
||||
<outlet property="submitButton" destination="k3J-Eg-itz" id="fiZ-wK-6YM"/>
|
||||
<outlet property="submitButtonMinLeadingConstraint" destination="bEB-EO-b14" id="Iz5-ks-nSX"/>
|
||||
<outlet property="view" destination="5rn-KE-plm" id="bFJ-yJ-vc0"/>
|
||||
|
@ -345,17 +348,67 @@
|
|||
<constraint firstAttribute="trailing" secondItem="6yx-o1-vbD" secondAttribute="trailing" constant="19" id="rWk-Mp-KPS"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vX2-5Y-rQc">
|
||||
<rect key="frame" x="0.0" y="96" width="375" height="170"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QYL-Lo-tmH">
|
||||
<rect key="frame" x="10" y="0.0" width="355" height="121"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="jFf-id-CJn"/>
|
||||
</constraints>
|
||||
<string key="text">Clear personal data
|
||||
|
||||
Warning: Your personal data (including encryption keys) is still stored on this device.
|
||||
|
||||
Clear it if you're finished using this device, or want to sign in to another account.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<string key="userLabel">Clear personal data Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.</string>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZRO-C2-hH1">
|
||||
<rect key="frame" x="245" y="129" width="119" height="30"/>
|
||||
<color key="backgroundColor" red="0.4624713659286499" green="0.81734329462051392" blue="0.47220504283905029" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="WjZ-Bh-No3"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="dvm-Bz-pT0"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="Clear all data">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="c0b-DK-MIg"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="ZRO-C2-hH1" firstAttribute="top" secondItem="QYL-Lo-tmH" secondAttribute="bottom" constant="8" id="29j-dP-GC6"/>
|
||||
<constraint firstItem="QYL-Lo-tmH" firstAttribute="leading" secondItem="vX2-5Y-rQc" secondAttribute="leading" constant="10" id="3Qr-IT-GDa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ZRO-C2-hH1" secondAttribute="trailing" constant="11" id="Cj8-Kx-8vu"/>
|
||||
<constraint firstAttribute="trailing" secondItem="QYL-Lo-tmH" secondAttribute="trailing" constant="10" id="JoV-ju-IhL"/>
|
||||
<constraint firstItem="QYL-Lo-tmH" firstAttribute="top" secondItem="vX2-5Y-rQc" secondAttribute="top" id="Xjs-ZO-7fl"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="170" id="oUw-Jb-GkG"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ZRO-C2-hH1" secondAttribute="bottom" constant="11" id="q0c-yG-I0n"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCOptionsContainer"/>
|
||||
<constraints>
|
||||
<constraint firstItem="FIn-2w-e6H" firstAttribute="leading" secondItem="Gg0-TE-OGb" secondAttribute="leading" id="3G8-Tb-KaN"/>
|
||||
<constraint firstItem="wEJ-AF-rdH" firstAttribute="width" secondItem="k3J-Eg-itz" secondAttribute="width" id="7sB-YJ-eX4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vX2-5Y-rQc" secondAttribute="trailing" id="DPh-Jx-WP1"/>
|
||||
<constraint firstItem="vX2-5Y-rQc" firstAttribute="top" secondItem="Gg0-TE-OGb" secondAttribute="top" priority="100" id="QSG-jB-VcV"/>
|
||||
<constraint firstItem="wEJ-AF-rdH" firstAttribute="centerY" secondItem="k3J-Eg-itz" secondAttribute="centerY" id="Vze-EI-oXj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k3J-Eg-itz" secondAttribute="trailing" constant="11" id="ZyA-Tq-Sfq"/>
|
||||
<constraint firstItem="k3J-Eg-itz" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Gg0-TE-OGb" secondAttribute="leading" constant="150" id="bEB-EO-b14"/>
|
||||
<constraint firstItem="vX2-5Y-rQc" firstAttribute="top" secondItem="k3J-Eg-itz" secondAttribute="bottom" constant="33" id="db7-2y-vPZ"/>
|
||||
<constraint firstItem="AJ2-lJ-NUq" firstAttribute="centerY" secondItem="k3J-Eg-itz" secondAttribute="centerY" id="dcE-Vs-7Rt"/>
|
||||
<constraint firstAttribute="trailing" secondItem="FIn-2w-e6H" secondAttribute="trailing" id="kFj-6g-v3H"/>
|
||||
<constraint firstItem="vX2-5Y-rQc" firstAttribute="leading" secondItem="Gg0-TE-OGb" secondAttribute="leading" id="kYN-Lj-zYP"/>
|
||||
<constraint firstAttribute="height" constant="300" id="lXv-gM-CjN"/>
|
||||
<constraint firstItem="k3J-Eg-itz" firstAttribute="top" secondItem="Gg0-TE-OGb" secondAttribute="top" constant="33" id="mor-t9-7Ke"/>
|
||||
<constraint firstItem="FIn-2w-e6H" firstAttribute="top" secondItem="k3J-Eg-itz" secondAttribute="bottom" constant="5" id="oTS-5o-MMW"/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -54,6 +55,7 @@
|
|||
@end
|
||||
|
||||
@implementation AuthInputsView
|
||||
@synthesize softLogoutCredentials;
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
|
@ -487,6 +489,15 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For soft logout, pass the device_id currently used
|
||||
if (parameters && self.softLogoutCredentials)
|
||||
{
|
||||
NSMutableDictionary *parametersWithDeviceId = [parameters mutableCopy];
|
||||
parametersWithDeviceId[@"device_id"] = self.softLogoutCredentials.deviceId;
|
||||
parameters = parametersWithDeviceId;
|
||||
}
|
||||
|
||||
}
|
||||
else if (type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
|
@ -725,7 +736,12 @@
|
|||
{
|
||||
// Note: this use case was not tested yet.
|
||||
parameters = @{
|
||||
@"auth": @{@"session":currentSession.session, @"username": self.userLoginTextField.text, @"password": self.passWordTextField.text, @"type": kMXLoginFlowTypePassword}
|
||||
@"auth": @{
|
||||
@"session":currentSession.session,
|
||||
@"username": self.userLoginTextField.text,
|
||||
@"password": self.passWordTextField.text,
|
||||
@"type": kMXLoginFlowTypePassword
|
||||
}
|
||||
};
|
||||
}
|
||||
else if ([self isFlowSupported:kMXLoginFlowTypeTerms] && ![self isFlowCompleted:kMXLoginFlowTypeTerms])
|
||||
|
@ -936,6 +952,71 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)setSoftLogoutCredentials:(MXCredentials *)credentials
|
||||
{
|
||||
softLogoutCredentials = credentials;
|
||||
self.userLoginTextField.text = softLogoutCredentials.userId;
|
||||
self.userLoginContainer.hidden = YES;
|
||||
self.phoneContainer.hidden = YES;
|
||||
|
||||
[self displaySoftLogoutMessage];
|
||||
}
|
||||
|
||||
- (void)displaySoftLogoutMessage
|
||||
{
|
||||
// Take some shortcuts and make some assumptions (Riot uses MXFileStore and MXRealmCryptoStore) to
|
||||
// retrieve data to display as quick as possible
|
||||
MXRealmCryptoStore *cryptoStore = [[MXRealmCryptoStore alloc] initWithCredentials:self.softLogoutCredentials];
|
||||
BOOL keyBackupNeeded = [cryptoStore inboundGroupSessionsToBackup:1].count > 0;
|
||||
|
||||
MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:softLogoutCredentials];
|
||||
[fileStore asyncUsersWithUserIds:@[softLogoutCredentials.userId] success:^(NSArray<MXUser *> * _Nonnull users) {
|
||||
|
||||
MXUser *myUser = users.firstObject;
|
||||
[fileStore close];
|
||||
|
||||
[self displaySoftLogoutMessageWithUserDisplayname:myUser.displayname andKeyBackupNeeded:keyBackupNeeded];
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
NSLog(@"[AuthInputsView] displaySoftLogoutMessage: Cannot load displayname. Error: %@", error);
|
||||
[self displaySoftLogoutMessageWithUserDisplayname:nil andKeyBackupNeeded:keyBackupNeeded];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)displaySoftLogoutMessageWithUserDisplayname:(NSString*)userDisplayname andKeyBackupNeeded:(BOOL)keyBackupNeeded
|
||||
{
|
||||
// Use messageLabel for this message
|
||||
self.messageLabelTopConstraint.constant = 8;
|
||||
self.messageLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.messageLabel.hidden = NO;
|
||||
|
||||
NSMutableAttributedString *message = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_softlogout_sign_in", @"Vector", nil)
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]
|
||||
}];
|
||||
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];
|
||||
|
||||
NSString *string = [NSString stringWithFormat:NSLocalizedStringFromTable(@"auth_softlogout_reason", @"Vector", nil),
|
||||
softLogoutCredentials.homeServerName, userDisplayname, softLogoutCredentials.userId];
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:string
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
||||
}]];
|
||||
|
||||
if (keyBackupNeeded)
|
||||
{
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];
|
||||
string = NSLocalizedStringFromTable(@"auth_softlogout_recover_encryption_keys", @"Vector", nil);
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:string
|
||||
attributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
||||
}]];
|
||||
}
|
||||
|
||||
self.messageLabel.attributedText = message;
|
||||
}
|
||||
|
||||
- (BOOL)areAllRequiredFieldsSet
|
||||
{
|
||||
// Keep enable the submit button.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -15,7 +15,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="600" height="200"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ckH-mV-Yds">
|
||||
<rect key="frame" x="0.0" y="8.5" width="600" height="183"/>
|
||||
<rect key="frame" x="0.0" y="37.5" width="600" height="183"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fSJ-23-bFN">
|
||||
<rect key="frame" x="180" y="75.5" width="240" height="32"/>
|
||||
|
@ -284,6 +284,7 @@
|
|||
<constraint firstItem="rb1-L5-udI" firstAttribute="top" secondItem="x74-04-ezp" secondAttribute="top" constant="100" id="75U-tx-PsQ"/>
|
||||
<constraint firstItem="UfH-jv-6w4" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="7Bk-GF-MZ0"/>
|
||||
<constraint firstItem="IB7-1E-eeL" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="7Lr-fy-W8L"/>
|
||||
<constraint firstItem="ckH-mV-Yds" firstAttribute="top" relation="greaterThanOrEqual" secondItem="68j-f9-JG4" secondAttribute="bottom" constant="10" id="7aQ-ei-TrU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="68j-f9-JG4" secondAttribute="trailing" constant="10" id="8Aa-YT-MP5"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UfH-jv-6w4" secondAttribute="trailing" id="8dz-wY-Kxx"/>
|
||||
<constraint firstItem="whs-Ob-uzD" firstAttribute="centerX" secondItem="x74-04-ezp" secondAttribute="centerX" id="8lX-k1-85c"/>
|
||||
|
@ -293,7 +294,7 @@
|
|||
<constraint firstItem="bXz-VI-5FS" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="Frq-sH-HZT"/>
|
||||
<constraint firstItem="xOW-lo-QGC" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="NOu-LR-RvE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bXz-VI-5FS" secondAttribute="trailing" id="NiV-pJ-PfV"/>
|
||||
<constraint firstItem="ckH-mV-Yds" firstAttribute="centerY" secondItem="x74-04-ezp" secondAttribute="centerY" id="PNx-SK-FZY"/>
|
||||
<constraint firstItem="ckH-mV-Yds" firstAttribute="centerY" secondItem="x74-04-ezp" secondAttribute="centerY" priority="100" id="PNx-SK-FZY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xOW-lo-QGC" secondAttribute="trailing" id="SNm-WQ-Piu"/>
|
||||
<constraint firstItem="xOW-lo-QGC" firstAttribute="top" secondItem="x74-04-ezp" secondAttribute="top" id="WmX-gO-hPJ"/>
|
||||
<constraint firstItem="rb1-L5-udI" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="XAJ-ST-sWV"/>
|
||||
|
|
187
Riot/Modules/Camera/CameraPresenter.swift
Normal file
187
Riot/Modules/Camera/CameraPresenter.swift
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
@objc protocol CameraPresenterDelegate: class {
|
||||
func cameraPresenter(_ presenter: CameraPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?)
|
||||
func cameraPresenter(_ presenter: CameraPresenter, didSelectVideoAt url: URL)
|
||||
func cameraPresenterDidCancel(_ cameraPresenter: CameraPresenter)
|
||||
}
|
||||
|
||||
/// CameraPresenter enables to present native camera
|
||||
@objc final class CameraPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let jpegCompressionQuality: CGFloat = 1.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private weak var presentingViewController: UIViewController?
|
||||
private weak var cameraViewController: UIViewController?
|
||||
private var mediaUTIs: [MXKUTI] = []
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc weak var delegate: CameraPresenterDelegate?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc func presentCamera(from presentingViewController: UIViewController, with mediaUTIs: [MXKUTI], animated: Bool) {
|
||||
self.presentingViewController = presentingViewController
|
||||
self.mediaUTIs = mediaUTIs
|
||||
self.checkCameraPermissionAndPresentCamera(animated: animated)
|
||||
}
|
||||
|
||||
@objc func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let cameraViewController = self.cameraViewController else {
|
||||
return
|
||||
}
|
||||
cameraViewController.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func checkCameraPermissionAndPresentCamera(animated: Bool) {
|
||||
|
||||
let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
||||
|
||||
switch authorizationStatus {
|
||||
case .authorized:
|
||||
self.presentCameraController(animated: animated)
|
||||
case .notDetermined:
|
||||
self.requestCameraAccess(completion: { (granted) in
|
||||
if granted {
|
||||
self.presentCameraController(animated: animated)
|
||||
} else {
|
||||
self.presentPermissionDeniedAlert()
|
||||
}
|
||||
})
|
||||
case .denied, .restricted:
|
||||
self.presentPermissionDeniedAlert()
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func presentCameraController(animated: Bool) {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let cameraViewController = self.buildCameraViewController() else {
|
||||
return
|
||||
}
|
||||
|
||||
presentingViewController.present(cameraViewController, animated: true, completion: nil)
|
||||
self.cameraViewController = cameraViewController
|
||||
}
|
||||
|
||||
private func buildCameraViewController() -> UIViewController? {
|
||||
guard UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let mediaTypes = self.mediaUTIs.map { (uti) -> String in
|
||||
return uti.rawValue
|
||||
}
|
||||
|
||||
let imagePickerController = UIImagePickerController()
|
||||
imagePickerController.delegate = self
|
||||
imagePickerController.sourceType = UIImagePickerController.SourceType.camera
|
||||
imagePickerController.mediaTypes = mediaTypes
|
||||
imagePickerController.allowsEditing = false
|
||||
|
||||
return imagePickerController
|
||||
}
|
||||
|
||||
private func requestCameraAccess(completion: @escaping (_ granted: Bool) -> Void) {
|
||||
AVCaptureDevice.requestAccess(for: .video) { granted in
|
||||
DispatchQueue.main.async {
|
||||
completion(granted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentPermissionDeniedAlert() {
|
||||
guard let presentingViewController = self.presentingViewController, let settingsURL = URL(string: UIApplication.openSettingsURLString) else {
|
||||
return
|
||||
}
|
||||
|
||||
let appDisplayName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? ""
|
||||
|
||||
let alert = UIAlertController(title: VectorL10n.camera, message: VectorL10n.cameraAccessNotGranted(appDisplayName), preferredStyle: .alert)
|
||||
|
||||
let cancelActionTitle = Bundle.mxk_localizedString(forKey: "ok")
|
||||
let cancelAction = UIAlertAction(title: cancelActionTitle, style: .cancel, handler: { _ in
|
||||
})
|
||||
|
||||
let settingsActionTitle = Bundle.mxk_localizedString(forKey: "settings")
|
||||
let settingsAction = UIAlertAction(title: settingsActionTitle, style: .default, handler: { _ in
|
||||
UIApplication.shared.open(settingsURL, options: [:], completionHandler: { (succeed) in
|
||||
if !succeed {
|
||||
print("[CameraPresenter] Fails to open settings")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
alert.addAction(cancelAction)
|
||||
alert.addAction(settingsAction)
|
||||
|
||||
presentingViewController.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func presentCameraUnavailableAlert() {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let alert = UIAlertController(title: VectorL10n.camera, message: VectorL10n.cameraUnavailable, preferredStyle: .alert)
|
||||
|
||||
let okAction = UIAlertAction(title: VectorL10n.accept, style: .default, handler: nil)
|
||||
|
||||
alert.addAction(okAction)
|
||||
|
||||
presentingViewController.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIImagePickerControllerDelegate
|
||||
extension CameraPresenter: UIImagePickerControllerDelegate {
|
||||
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
|
||||
if let videoURL = info[.mediaURL] as? URL {
|
||||
self.delegate?.cameraPresenter(self, didSelectVideoAt: videoURL)
|
||||
} else if let image = (info[.editedImage] ?? info[.originalImage]) as? UIImage, let imageData = image.jpegData(compressionQuality: Constants.jpegCompressionQuality) {
|
||||
self.delegate?.cameraPresenter(self, didSelectImageData: imageData, withUTI: MXKUTI.jpeg)
|
||||
}
|
||||
}
|
||||
|
||||
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||
self.delegate?.cameraPresenterDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UINavigationControllerDelegate
|
||||
extension CameraPresenter: UINavigationControllerDelegate {
|
||||
}
|
|
@ -1205,7 +1205,7 @@
|
|||
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
||||
|
||||
// text color
|
||||
UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"];
|
||||
UITextField *searchBarTextField = searchBar.vc_searchTextField;
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
|
||||
// Magnifying glass icon.
|
||||
|
|
|
@ -606,7 +606,7 @@
|
|||
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
||||
|
||||
// text color
|
||||
UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"];
|
||||
UITextField *searchBarTextField = searchBar.vc_searchTextField;
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
|
||||
// Magnifying glass icon.
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<MediaAlbumContentViewControllerDelegate> delegate;
|
||||
@property (nonatomic, weak) id<MediaAlbumContentViewControllerDelegate> delegate;
|
||||
|
||||
/**
|
||||
The array of the media types listed by the view controller (default value is an array containing kUTTypeImage).
|
||||
|
|
|
@ -89,11 +89,15 @@
|
|||
self.mediaTypes = @[(NSString *)kUTTypeImage];
|
||||
}
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
// Observe UIApplicationWillEnterForegroundNotification to refresh captures collection when app leaves the background state.
|
||||
UIApplicationWillEnterForegroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
// Force a full refresh of the displayed collection
|
||||
self.assetsCollection = _assetsCollection;
|
||||
self.assetsCollection = self->_assetsCollection;
|
||||
|
||||
}];
|
||||
|
||||
|
@ -105,6 +109,8 @@
|
|||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
}];
|
||||
|
|
97
Riot/Modules/MediaPicker/MediaPickerCoordinator.swift
Normal file
97
Riot/Modules/MediaPicker/MediaPickerCoordinator.swift
Normal file
|
@ -0,0 +1,97 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Test MediaPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class MediaPickerCoordinator: NSObject, MediaPickerCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let mediaUTIs: [MXKUTI]
|
||||
private let allowsMultipleSelection: Bool
|
||||
|
||||
private let navigationRouter: NavigationRouterType
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: MediaPickerCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, mediaUTIs: [MXKUTI], allowsMultipleSelection: Bool) {
|
||||
self.session = session
|
||||
self.mediaUTIs = mediaUTIs
|
||||
self.allowsMultipleSelection = allowsMultipleSelection
|
||||
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let mediaTypes = self.mediaUTIs.map { (uti) -> String in
|
||||
return uti.rawValue
|
||||
}
|
||||
|
||||
let mediaPickerViewController: MediaPickerViewController = MediaPickerViewController.instantiate()
|
||||
mediaPickerViewController.mediaTypes = mediaTypes
|
||||
mediaPickerViewController.allowsMultipleSelection = self.allowsMultipleSelection
|
||||
self.navigationRouter.setRootModule(mediaPickerViewController)
|
||||
mediaPickerViewController.delegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MediaPickerViewControllerDelegate
|
||||
extension MediaPickerCoordinator: MediaPickerViewControllerDelegate {
|
||||
|
||||
func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectImage imageData: Data!, withMimeType mimetype: String!, isPhotoLibraryAsset: Bool) {
|
||||
|
||||
let uti: MXKUTI?
|
||||
if let mimetype = mimetype {
|
||||
uti = MXKUTI(mimeType: mimetype)
|
||||
} else {
|
||||
uti = nil
|
||||
}
|
||||
|
||||
self.delegate?.mediaPickerCoordinator(self, didSelectImageData: imageData, withUTI: uti)
|
||||
}
|
||||
|
||||
func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectVideo videoURL: URL!) {
|
||||
self.delegate?.mediaPickerCoordinator(self, didSelectVideoAt: videoURL)
|
||||
}
|
||||
|
||||
func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelect assets: [PHAsset]!) {
|
||||
self.delegate?.mediaPickerCoordinator(self, didSelectAssets: assets)
|
||||
}
|
||||
|
||||
func mediaPickerControllerDidCancel(_ mediaPickerController: MediaPickerViewController!) {
|
||||
self.delegate?.mediaPickerCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Test MediaPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc protocol MediaPickerCoordinatorBridgePresenterDelegate {
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?)
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideoAt url: URL)
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectAssets assets: [PHAsset])
|
||||
func mediaPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// MediaPickerCoordinatorBridgePresenter enables to start MediaPickerCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class MediaPickerCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let mediaUTIs: [MXKUTI]
|
||||
private let allowsMultipleSelection: Bool
|
||||
private var coordinator: MediaPickerCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: MediaPickerCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, mediaUTIs: [MXKUTI], allowsMultipleSelection: Bool) {
|
||||
self.session = session
|
||||
self.mediaUTIs = mediaUTIs
|
||||
self.allowsMultipleSelection = allowsMultipleSelection
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController,
|
||||
sourceView: UIView?,
|
||||
sourceRect: CGRect,
|
||||
animated: Bool) {
|
||||
let mediaPickerCoordinator = MediaPickerCoordinator(session: self.session, mediaUTIs: mediaUTIs, allowsMultipleSelection: self.allowsMultipleSelection)
|
||||
mediaPickerCoordinator.delegate = self
|
||||
|
||||
let mediaPickerPresentable = mediaPickerCoordinator.toPresentable()
|
||||
|
||||
if let sourceView = sourceView {
|
||||
|
||||
mediaPickerPresentable.modalPresentationStyle = .popover
|
||||
|
||||
if let popoverPresentationController = mediaPickerPresentable.popoverPresentationController {
|
||||
popoverPresentationController.sourceView = sourceView
|
||||
|
||||
let finalSourceRect: CGRect
|
||||
|
||||
if sourceRect != CGRect.null {
|
||||
finalSourceRect = sourceRect
|
||||
} else {
|
||||
finalSourceRect = sourceView.bounds
|
||||
}
|
||||
|
||||
popoverPresentationController.sourceRect = finalSourceRect
|
||||
}
|
||||
}
|
||||
|
||||
viewController.present(mediaPickerPresentable, animated: animated, completion: nil)
|
||||
|
||||
mediaPickerCoordinator.start()
|
||||
|
||||
self.coordinator = mediaPickerCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MediaPickerCoordinatorDelegate
|
||||
extension MediaPickerCoordinatorBridgePresenter: MediaPickerCoordinatorDelegate {
|
||||
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) {
|
||||
self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectImageData: imageData, withUTI: uti)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL) {
|
||||
self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectVideoAt: url)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset]) {
|
||||
self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectAssets: assets)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinatorDidCancel(_ coordinator: MediaPickerCoordinatorType) {
|
||||
self.delegate?.mediaPickerCoordinatorBridgePresenterDidCancel(self)
|
||||
}
|
||||
}
|
31
Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift
Normal file
31
Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift
Normal file
|
@ -0,0 +1,31 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Test MediaPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol MediaPickerCoordinatorDelegate: class {
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectImageData imageData: Data, withUTI uti: MXKUTI?)
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL)
|
||||
func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset])
|
||||
func mediaPickerCoordinatorDidCancel(_ coordinator: MediaPickerCoordinatorType)
|
||||
}
|
||||
|
||||
/// `MediaPickerCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
|
||||
protocol MediaPickerCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: MediaPickerCoordinatorDelegate? { get }
|
||||
}
|
|
@ -43,6 +43,13 @@
|
|||
*/
|
||||
- (void)mediaPickerController:(MediaPickerViewController *)mediaPickerController didSelectVideo:(NSURL*)videoURL;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user wants to cancel media picking.
|
||||
|
||||
@param mediaPickerController the `MediaPickerViewController` instance.
|
||||
*/
|
||||
- (void)mediaPickerControllerDidCancel:(MediaPickerViewController *)mediaPickerController;
|
||||
|
||||
@optional
|
||||
/**
|
||||
Tells the delegate that the user select multiple media.
|
||||
|
@ -55,16 +62,9 @@
|
|||
@end
|
||||
|
||||
/**
|
||||
* MediaPickerViewController displays recent camera captures and photo/video albums from user library.
|
||||
*/
|
||||
@interface MediaPickerViewController : MXKViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, AVCaptureFileOutputRecordingDelegate, MediaAlbumContentViewControllerDelegate>
|
||||
|
||||
/**
|
||||
* Returns the `UINib` object initialized for a `MediaPickerViewController`.
|
||||
*
|
||||
* @return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
* or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
@interface MediaPickerViewController : MXKViewController
|
||||
|
||||
/**
|
||||
* Creates and returns a new `MediaPickerViewController` object.
|
||||
|
@ -73,7 +73,7 @@
|
|||
*
|
||||
* @return An initialized `MediaPickerViewController` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)mediaPickerViewController;
|
||||
+ (instancetype)instantiate;
|
||||
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
|
@ -85,5 +85,11 @@
|
|||
*/
|
||||
@property (nonatomic) NSArray *mediaTypes;
|
||||
|
||||
/**
|
||||
A Boolean value that determines whether users can select more than one item.
|
||||
Default is NO.
|
||||
*/
|
||||
@property (nonatomic) BOOL allowsMultipleSelection;
|
||||
|
||||
@end
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,26 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina5_5" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<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" customClass="MediaPickerViewController">
|
||||
<connections>
|
||||
<outlet property="cameraActivityIndicator" destination="dxP-iB-dWk" id="HBu-hw-Cx9"/>
|
||||
<outlet property="cameraCaptureButton" destination="uRG-b0-CjY" id="3Qc-PU-LUY"/>
|
||||
<outlet property="cameraCaptureButtonWidthConstraint" destination="gmN-48-HHd" id="NQ1-3b-Oz7"/>
|
||||
<outlet property="cameraPreviewContainerAspectRatio" destination="QDd-Tq-4zL" id="qNb-5w-GnM"/>
|
||||
<outlet property="cameraPreviewContainerView" destination="w7z-f3-kdT" id="iqZ-I1-ZcW"/>
|
||||
<outlet property="cameraSwitchButton" destination="PXk-ZD-TYS" id="i6p-yB-8HG"/>
|
||||
<outlet property="cameraVideoCaptureProgressView" destination="aXP-Vh-zUp" id="yvj-G0-8oa"/>
|
||||
<outlet property="captureViewContainer" destination="cE0-0g-Su5" id="cr5-6S-9rj"/>
|
||||
<outlet property="closeButton" destination="sO8-Ds-mXZ" id="lkx-5W-NN9"/>
|
||||
<outlet property="libraryViewContainer" destination="QeN-lu-6hK" id="hHu-hn-fXS"/>
|
||||
<outlet property="libraryViewContainerViewHeightConstraint" destination="aRE-Ox-5IJ" id="6mr-qM-Oge"/>
|
||||
<outlet property="mainScrollView" destination="ZfS-wG-RjJ" id="Jjn-q7-ggw"/>
|
||||
|
@ -37,7 +28,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZfS-wG-RjJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<rect key="frame" x="0.0" y="20" width="414" height="716"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cE0-0g-Su5" userLabel="Capture View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="607.33333333333337"/>
|
||||
|
@ -58,9 +49,6 @@
|
|||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="xHg-kl-L3w"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="camera_switch" translatesAutoresizingMaskIntoConstraints="NO" id="etc-h5-0u2">
|
||||
<rect key="frame" x="363" y="35" width="32" height="24"/>
|
||||
|
@ -80,9 +68,6 @@
|
|||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="nvH-aw-ZJK"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="remove_icon" translatesAutoresizingMaskIntoConstraints="NO" id="VA2-Cu-dX6">
|
||||
<rect key="frame" x="24" y="36" width="22" height="22"/>
|
||||
|
@ -92,10 +77,10 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="dxP-iB-dWk" userLabel="Camera Activity Indicator">
|
||||
<rect key="frame" x="197" y="294" width="20" height="20"/>
|
||||
<rect key="frame" x="197" y="293.66666666666669" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aXP-Vh-zUp" customClass="MXKPieChartView">
|
||||
<rect key="frame" x="161" y="490" width="92" height="92"/>
|
||||
<rect key="frame" x="161" y="489.66666666666674" width="92" height="92"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MediaPickerVCCameraVideoCaptureProgressView"/>
|
||||
<constraints>
|
||||
|
@ -114,9 +99,6 @@
|
|||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="camera_capture"/>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="R4P-UW-q4j"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<accessibility key="accessibilityConfiguration" identifier="accessibilityIdentifierCaptureView"/>
|
||||
|
@ -143,7 +125,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jzs-FL-Rqd" userLabel="Collection Container View">
|
||||
<rect key="frame" x="0.0" y="607.33333333333337" width="414" height="450.00000000000011"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="450"/>
|
||||
<subviews>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="Cnz-mP-gE5">
|
||||
<rect key="frame" x="0.0" y="2" width="414" height="448"/>
|
||||
|
@ -173,7 +155,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QeN-lu-6hK" userLabel="Library View">
|
||||
<rect key="frame" x="0.0" y="1057.3333333333333" width="414" height="74"/>
|
||||
<rect key="frame" x="0.0" y="450" width="414" height="74"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="74" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="mCG-GH-ADc" userLabel="Albums Table View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="74"/>
|
||||
|
@ -199,6 +181,7 @@
|
|||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="cE0-0g-Su5" firstAttribute="top" secondItem="ZfS-wG-RjJ" secondAttribute="top" id="1Kz-qe-p3Y"/>
|
||||
<constraint firstItem="jzs-FL-Rqd" firstAttribute="width" secondItem="ZfS-wG-RjJ" secondAttribute="width" id="1fS-n9-T98"/>
|
||||
<constraint firstItem="QeN-lu-6hK" firstAttribute="top" secondItem="jzs-FL-Rqd" secondAttribute="bottom" id="3az-9S-rxJ"/>
|
||||
<constraint firstItem="cE0-0g-Su5" firstAttribute="centerX" secondItem="ZfS-wG-RjJ" secondAttribute="centerX" id="Fqd-7G-H68"/>
|
||||
<constraint firstItem="cE0-0g-Su5" firstAttribute="leading" secondItem="ZfS-wG-RjJ" secondAttribute="leading" id="IZE-p0-8AN"/>
|
||||
|
@ -208,12 +191,18 @@
|
|||
<constraint firstAttribute="trailing" secondItem="jzs-FL-Rqd" secondAttribute="trailing" id="g7G-DC-uSR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="QeN-lu-6hK" secondAttribute="trailing" id="kbe-Uh-tug"/>
|
||||
<constraint firstItem="jzs-FL-Rqd" firstAttribute="top" secondItem="cE0-0g-Su5" secondAttribute="bottom" id="q35-tT-GBd"/>
|
||||
<constraint firstItem="jzs-FL-Rqd" firstAttribute="top" secondItem="ZfS-wG-RjJ" secondAttribute="top" id="qwZ-4d-IVv"/>
|
||||
<constraint firstItem="cE0-0g-Su5" firstAttribute="height" relation="lessThanOrEqual" secondItem="ZfS-wG-RjJ" secondAttribute="height" id="w7s-40-3O5"/>
|
||||
<constraint firstItem="jzs-FL-Rqd" firstAttribute="leading" secondItem="ZfS-wG-RjJ" secondAttribute="leading" id="wZf-nP-JTD"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="MediaPickerVCMainScrollView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<variation key="default">
|
||||
<mask key="subviews">
|
||||
<exclude reference="cE0-0g-Su5"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-1" id="Wso-Tg-NuE"/>
|
||||
</connections>
|
||||
|
@ -222,16 +211,18 @@
|
|||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MediaPickerVCView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="ZfS-wG-RjJ" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="BJx-Hb-D6M"/>
|
||||
<constraint firstItem="a75-G6-NXm" firstAttribute="top" secondItem="ZfS-wG-RjJ" secondAttribute="top" id="K0g-Ml-d3i"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ZfS-wG-RjJ" secondAttribute="bottom" id="Sre-5e-ocp"/>
|
||||
<constraint firstItem="ZfS-wG-RjJ" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="TFD-H5-ucr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ZfS-wG-RjJ" secondAttribute="trailing" id="UrC-gh-xoR"/>
|
||||
<constraint firstItem="ZfS-wG-RjJ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="bUn-mA-Mv2"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="a75-G6-NXm"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="camera_capture" width="83" height="83"/>
|
||||
<image name="camera_switch" width="32" height="24"/>
|
||||
<image name="camera_switch" width="31.333333969116211" height="23.666666030883789"/>
|
||||
<image name="remove_icon" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
150
Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift
Normal file
150
Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
@objc protocol SingleImagePickerPresenterDelegate: class {
|
||||
func singleImagePickerPresenter(_ presenter: SingleImagePickerPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?)
|
||||
func singleImagePickerPresenterDidCancel(_ presenter: SingleImagePickerPresenter)
|
||||
}
|
||||
|
||||
/// SingleImagePickerPresenter enables to present an image picker with single selection
|
||||
@objcMembers
|
||||
final class SingleImagePickerPresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
|
||||
private weak var presentingViewController: UIViewController?
|
||||
private var cameraPresenter: CameraPresenter?
|
||||
private var mediaPickerPresenter: MediaPickerCoordinatorBridgePresenter?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: SingleImagePickerPresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from presentingViewController: UIViewController,
|
||||
sourceView: UIView?,
|
||||
sourceRect: CGRect,
|
||||
animated: Bool) {
|
||||
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
|
||||
let cameraAction = UIAlertAction(title: VectorL10n.imagePickerActionCamera, style: .default, handler: { _ in
|
||||
self.presentCamera(animated: animated)
|
||||
})
|
||||
|
||||
let photoLibraryAction = UIAlertAction(title: VectorL10n.imagePickerActionLibrary, style: .default, handler: { _ in
|
||||
self.presentPhotoLibray(sourceView: sourceView, sourceRect: sourceRect, animated: animated)
|
||||
})
|
||||
|
||||
let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: { _ in
|
||||
})
|
||||
|
||||
alert.addAction(cameraAction)
|
||||
alert.addAction(photoLibraryAction)
|
||||
alert.addAction(cancelAction)
|
||||
|
||||
if let popoverPresentationController = alert.popoverPresentationController {
|
||||
popoverPresentationController.sourceView = sourceView
|
||||
popoverPresentationController.sourceRect = sourceRect
|
||||
}
|
||||
|
||||
presentingViewController.present(alert, animated: animated, completion: nil)
|
||||
self.presentingViewController = presentingViewController
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
if let cameraPresenter = self.cameraPresenter {
|
||||
cameraPresenter.dismiss(animated: animated, completion: completion)
|
||||
} else if let mediaPickerPresenter = self.mediaPickerPresenter {
|
||||
mediaPickerPresenter.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentCamera(animated: Bool) {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let cameraPresenter = CameraPresenter()
|
||||
cameraPresenter.delegate = self
|
||||
cameraPresenter.presentCamera(from: presentingViewController, with: [.image], animated: animated)
|
||||
self.cameraPresenter = cameraPresenter
|
||||
}
|
||||
|
||||
private func presentPhotoLibray(sourceView: UIView?, sourceRect: CGRect, animated: Bool) {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let mediaPickerPresenter = MediaPickerCoordinatorBridgePresenter(session: self.session, mediaUTIs: [.image], allowsMultipleSelection: false)
|
||||
mediaPickerPresenter.delegate = self
|
||||
|
||||
mediaPickerPresenter.present(from: presentingViewController, sourceView: sourceView, sourceRect: sourceRect, animated: animated)
|
||||
self.mediaPickerPresenter = mediaPickerPresenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - CameraPresenterDelegate
|
||||
extension SingleImagePickerPresenter: CameraPresenterDelegate {
|
||||
|
||||
func cameraPresenter(_ cameraPresenter: CameraPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) {
|
||||
self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti)
|
||||
}
|
||||
|
||||
func cameraPresenterDidCancel(_ cameraPresenter: CameraPresenter) {
|
||||
self.delegate?.singleImagePickerPresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func cameraPresenter(_ cameraPresenter: CameraPresenter, didSelectVideoAt url: URL) {
|
||||
self.delegate?.singleImagePickerPresenterDidCancel(self)
|
||||
}
|
||||
}
|
||||
// MARK: - MediaPickerCoordinatorBridgePresenterDelegate
|
||||
extension SingleImagePickerPresenter: MediaPickerCoordinatorBridgePresenterDelegate {
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) {
|
||||
self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideoAt url: URL) {
|
||||
self.delegate?.singleImagePickerPresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectAssets assets: [PHAsset]) {
|
||||
self.delegate?.singleImagePickerPresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func mediaPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter) {
|
||||
self.delegate?.singleImagePickerPresenterDidCancel(self)
|
||||
}
|
||||
}
|
|
@ -54,20 +54,8 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable {
|
|||
// MARK: - Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.collectionView.isScrollEnabled = false
|
||||
self.collectionView.delegate = self
|
||||
self.collectionView.dataSource = self
|
||||
self.collectionView.collectionViewLayout = DGCollectionViewLeftAlignFlowLayout()
|
||||
|
||||
if let collectionViewFlowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
|
||||
collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
|
||||
collectionViewFlowLayout.minimumInteritemSpacing = Constants.minimumInteritemSpacing
|
||||
collectionViewFlowLayout.minimumLineSpacing = Constants.minimumLineSpacing
|
||||
}
|
||||
|
||||
self.collectionView.register(cellType: BubbleReactionViewCell.self)
|
||||
self.collectionView.register(cellType: BubbleReactionActionViewCell.self)
|
||||
self.collectionView.reloadData()
|
||||
self.setupCollectionView()
|
||||
self.setupLongPressGestureRecognizer()
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
|
@ -95,6 +83,36 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable {
|
|||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupCollectionView() {
|
||||
self.collectionView.isScrollEnabled = false
|
||||
self.collectionView.delegate = self
|
||||
self.collectionView.dataSource = self
|
||||
self.collectionView.collectionViewLayout = DGCollectionViewLeftAlignFlowLayout()
|
||||
|
||||
if let collectionViewFlowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
|
||||
collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
|
||||
collectionViewFlowLayout.minimumInteritemSpacing = Constants.minimumInteritemSpacing
|
||||
collectionViewFlowLayout.minimumLineSpacing = Constants.minimumLineSpacing
|
||||
}
|
||||
|
||||
self.collectionView.register(cellType: BubbleReactionViewCell.self)
|
||||
self.collectionView.register(cellType: BubbleReactionActionViewCell.self)
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
|
||||
private func setupLongPressGestureRecognizer() {
|
||||
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
|
||||
gestureRecognizer.delaysTouchesBegan = true
|
||||
self.collectionView.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
@objc private func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||
guard gestureRecognizer.state == .began else {
|
||||
return
|
||||
}
|
||||
self.viewModel?.process(viewAction: .longPress)
|
||||
}
|
||||
|
||||
private func fill(reactionsViewData: [BubbleReactionViewData], showAllButtonState: BubbleReactionsViewState.ShowAllButtonState) {
|
||||
self.reactionsViewData = reactionsViewData
|
||||
self.showAllButtonState = showAllButtonState
|
||||
|
|
|
@ -69,6 +69,8 @@ import Foundation
|
|||
self.viewModelDelegate?.bubbleReactionsViewModel(self, didShowAllTappedForEventId: self.eventId)
|
||||
case .tapShowAction(.showLess):
|
||||
self.viewModelDelegate?.bubbleReactionsViewModel(self, didShowLessTappedForEventId: self.eventId)
|
||||
case .longPress:
|
||||
self.viewModelDelegate?.bubbleReactionsViewModel(self, didLongPressForEventId: self.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ enum BubbleReactionsViewAction {
|
|||
case tapReaction(index: Int)
|
||||
case addNewReaction
|
||||
case tapShowAction(action: ShowAction)
|
||||
case longPress
|
||||
|
||||
enum ShowAction {
|
||||
case showAll
|
||||
|
@ -43,6 +44,7 @@ enum BubbleReactionsViewState {
|
|||
func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didRemoveReaction reactionCount: MXReactionCount, forEventId eventId: String)
|
||||
func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didShowAllTappedForEventId eventId: String)
|
||||
func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didShowLessTappedForEventId eventId: String)
|
||||
func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didLongPressForEventId eventId: String)
|
||||
}
|
||||
|
||||
protocol BubbleReactionsViewModelViewDelegate: class {
|
||||
|
|
|
@ -30,7 +30,9 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
|||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var reactionsBackgroundView: UIView!
|
||||
@IBOutlet private weak var reactionsStackView: UIStackView!
|
||||
@IBOutlet private weak var reactionsStackView: UIStackView!
|
||||
@IBOutlet private weak var moreReactionsBackgroundView: UIView!
|
||||
@IBOutlet private weak var moreReactionsButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
|
@ -57,6 +59,10 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
|||
super.awakeFromNib()
|
||||
|
||||
self.reactionsBackgroundView.layer.masksToBounds = true
|
||||
|
||||
let moreReactionsImage = Asset.Images.moreReactions.image.withRenderingMode(.alwaysTemplate)
|
||||
self.moreReactionsButton.setImage(moreReactionsImage, for: .normal)
|
||||
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
|
@ -64,12 +70,15 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
|||
super.layoutSubviews()
|
||||
|
||||
self.reactionsBackgroundView.layer.cornerRadius = self.reactionsBackgroundView.frame.size.height/2
|
||||
self.moreReactionsBackgroundView.layer.cornerRadius = self.moreReactionsBackgroundView.frame.size.height/2
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.reactionsBackgroundView.backgroundColor = theme.headerBackgroundColor
|
||||
self.moreReactionsBackgroundView.backgroundColor = theme.headerBackgroundColor
|
||||
self.moreReactionsButton.tintColor = theme.textPrimaryColor
|
||||
}
|
||||
|
||||
func selectionAnimationInstructionPart1() {
|
||||
|
@ -132,6 +141,10 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
|||
self.tappedReactionButton = sender
|
||||
self.viewModel?.process(viewAction: .tap(reaction: tappedReaction))
|
||||
}
|
||||
|
||||
@IBAction private func moreReactionsAction(_ sender: Any) {
|
||||
self.viewModel?.process(viewAction: .moreReactions)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReactionsMenuViewModelViewDelegate
|
||||
|
|
|
@ -16,36 +16,66 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eXy-Kc-Ck7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="459" height="59"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="59"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="uzd-VB-mKT">
|
||||
<rect key="frame" x="5" y="5" width="449" height="49"/>
|
||||
<rect key="frame" x="5" y="5" width="385" height="49"/>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="uzd-VB-mKT" secondAttribute="trailing" constant="5" id="fDZ-NX-EyG"/>
|
||||
<constraint firstItem="uzd-VB-mKT" firstAttribute="leading" secondItem="eXy-Kc-Ck7" secondAttribute="leading" constant="5" id="iCa-Ob-e7J"/>
|
||||
<constraint firstAttribute="trailing" secondItem="uzd-VB-mKT" secondAttribute="trailing" constant="5" id="lG6-Qg-UcU"/>
|
||||
<constraint firstItem="uzd-VB-mKT" firstAttribute="top" secondItem="eXy-Kc-Ck7" secondAttribute="top" constant="5" id="msb-Ay-Bp6"/>
|
||||
<constraint firstAttribute="bottom" secondItem="uzd-VB-mKT" secondAttribute="bottom" constant="5" id="nJE-Gm-Rf1"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="44R-pM-RoU">
|
||||
<rect key="frame" x="400" y="0.0" width="59" height="59"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VQg-zY-j1F">
|
||||
<rect key="frame" x="0.0" y="0.0" width="59" height="59"/>
|
||||
<state key="normal" image="more_reactions">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="moreReactionsAction:" destination="7Xq-Wy-z0M" eventType="touchUpInside" id="09O-bn-ezs"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="VQg-zY-j1F" firstAttribute="top" secondItem="44R-pM-RoU" secondAttribute="top" id="Ku5-Ra-6tc"/>
|
||||
<constraint firstItem="VQg-zY-j1F" firstAttribute="leading" secondItem="44R-pM-RoU" secondAttribute="leading" id="YpY-1A-KhO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="VQg-zY-j1F" secondAttribute="trailing" id="aYC-Po-gf0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="VQg-zY-j1F" secondAttribute="bottom" id="fkS-hS-S5L"/>
|
||||
<constraint firstAttribute="width" secondItem="44R-pM-RoU" secondAttribute="height" multiplier="1:1" id="vLH-qD-4s0"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="eXy-Kc-Ck7" firstAttribute="top" secondItem="7Xq-Wy-z0M" secondAttribute="top" id="43J-YM-JHg"/>
|
||||
<constraint firstItem="eXy-Kc-Ck7" firstAttribute="leading" secondItem="7Xq-Wy-z0M" secondAttribute="leading" id="9dy-sK-ygm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eXy-Kc-Ck7" secondAttribute="trailing" id="E4Z-bt-BRw"/>
|
||||
<constraint firstAttribute="bottom" secondItem="eXy-Kc-Ck7" secondAttribute="bottom" id="Nb2-0z-IiS"/>
|
||||
<constraint firstItem="44R-pM-RoU" firstAttribute="top" secondItem="7Xq-Wy-z0M" secondAttribute="top" id="aLI-UY-XKE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="44R-pM-RoU" secondAttribute="trailing" id="fKs-oS-uui"/>
|
||||
<constraint firstAttribute="bottom" secondItem="44R-pM-RoU" secondAttribute="bottom" id="lE1-4e-whG"/>
|
||||
<constraint firstItem="44R-pM-RoU" firstAttribute="leading" secondItem="eXy-Kc-Ck7" secondAttribute="trailing" constant="5" id="zFB-XG-OKN"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="moreReactionsBackgroundView" destination="44R-pM-RoU" id="0Y4-Pt-1Zh"/>
|
||||
<outlet property="moreReactionsButton" destination="VQg-zY-j1F" id="ZfR-3o-lPV"/>
|
||||
<outlet property="reactionsBackgroundView" destination="eXy-Kc-Ck7" id="VYi-YD-mb9"/>
|
||||
<outlet property="reactionsStackView" destination="uzd-VB-mKT" id="DTV-Nh-bcm"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="444.20289855072468" y="-718.19196428571422"/>
|
||||
<point key="canvasLocation" x="296" y="-854"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="more_reactions" width="22" height="20"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -20,4 +20,5 @@ import UIKit
|
|||
enum ReactionsMenuViewAction {
|
||||
case loadData
|
||||
case tap(reaction: String)
|
||||
case moreReactions
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ import Foundation
|
|||
self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reaction, forEventId: self.eventId)
|
||||
}
|
||||
}
|
||||
case .moreReactions:
|
||||
self.coordinatorDelegate?.reactionsMenuViewModelDidTapMoreReactions(self, forEventId: self.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ protocol ReactionsMenuViewModelViewDelegate: class {
|
|||
@objc protocol ReactionsMenuViewModelCoordinatorDelegate: class {
|
||||
func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didAddReaction reaction: String, forEventId eventId: String)
|
||||
func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didRemoveReaction reaction: String, forEventId eventId: String)
|
||||
func reactionsMenuViewModelDidTapMoreReactions(_ viewModel: ReactionsMenuViewModel, forEventId eventId: String)
|
||||
}
|
||||
|
||||
protocol ReactionsMenuViewModelType {
|
||||
|
|
|
@ -201,6 +201,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
|
|||
|
||||
func update(theme: Theme) {
|
||||
self.menuToolbarView.update(theme: theme)
|
||||
self.reactionsMenuView?.update(theme: theme)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
@ -225,6 +226,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
|
|||
if self.reactionsMenuContainerView.subviews.isEmpty {
|
||||
let reactionsMenuView = ReactionsMenuView.loadFromNib()
|
||||
self.reactionsMenuContainerView.vc_addSubViewMatchingParent(reactionsMenuView)
|
||||
reactionsMenuView.update(theme: self.theme)
|
||||
self.reactionsMenuView = reactionsMenuView
|
||||
}
|
||||
|
||||
|
|
|
@ -238,12 +238,12 @@
|
|||
continue;
|
||||
}
|
||||
|
||||
MXAggregatedReactions* reactions = cellData.reactions[componentEventId];
|
||||
MXAggregatedReactions* reactions = cellData.reactions[componentEventId].aggregatedReactionsWithNonZeroCount;
|
||||
|
||||
BubbleReactionsView *reactionsView;
|
||||
|
||||
if (reactions && !isCollapsableCellCollapsed)
|
||||
{
|
||||
if (!component.event.isRedactedEvent && reactions && !isCollapsableCellCollapsed)
|
||||
{
|
||||
BOOL showAllReactions = [cellData showAllReactionsForEvent:componentEventId];
|
||||
BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions
|
||||
eventId:componentEventId
|
||||
|
@ -607,4 +607,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didLongPressForEventId:(NSString *)eventId
|
||||
{
|
||||
[self.delegate dataSource:self didRecognizeAction:kMXKRoomBubbleCellLongPressOnReactionView inCell:nil userInfo:@{ kMXKRoomBubbleCellEventIdKey: eventId }];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -65,9 +65,8 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject {
|
|||
let editHistoryCoordinator = EditHistoryCoordinator(session: self.session, formatter: formatter, event: self.event)
|
||||
editHistoryCoordinator.delegate = self
|
||||
|
||||
let navigationController = RiotNavigationController()
|
||||
let navigationController = RiotNavigationController(rootViewController: editHistoryCoordinator.toPresentable())
|
||||
navigationController.modalPresentationStyle = .formSheet
|
||||
navigationController.addChild(editHistoryCoordinator.toPresentable())
|
||||
viewController.present(navigationController, animated: animated, completion: nil)
|
||||
|
||||
editHistoryCoordinator.start()
|
||||
|
|
|
@ -113,7 +113,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType {
|
|||
sself.nextBatch = response.nextBatch
|
||||
sself.operation = nil
|
||||
|
||||
sself.process(editEvents: response.chunk,originalEvent: response.originalEvent, nextBatch: response.nextBatch)
|
||||
sself.process(editEvents: response.chunk, originalEvent: response.originalEvent, nextBatch: response.nextBatch)
|
||||
|
||||
}, failure: { [weak self] error in
|
||||
guard let sself = self else {
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
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 EmojiItem: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiItem properties.
|
||||
/// See https://github.com/missive/emoji-mart/blob/master/src/utils/data.js for minified letters informations.
|
||||
///
|
||||
/// - shortName: The commonly-agreed short name for the emoji, as supported in GitHub and others via the :short_name: syntax.
|
||||
/// - name: The offical Unicode name.
|
||||
/// - codepoint: The Unicode codepoint, as 4-5 hex digits. Where an emoji needs 2 or more codepoints, they are specified like 1F1EA-1F1F8.
|
||||
/// - shortNames: An array of all the other known short names.
|
||||
/// - keywords: Associated emoji keywords.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case shortName
|
||||
case name = "a"
|
||||
case codepoint = "b"
|
||||
case shortNames = "n"
|
||||
case keywords = "j"
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
guard let shortName = decoder.codingPath.last?.stringValue else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .shortName, in: container, debugDescription: "Cannot initialize short name")
|
||||
}
|
||||
|
||||
let emojiUnicodeStringValue = try container.decode(String.self, forKey: .codepoint)
|
||||
|
||||
let unicodeStringComponents = emojiUnicodeStringValue.components(separatedBy: "-")
|
||||
|
||||
var emoji = ""
|
||||
|
||||
for unicodeStringComponent in unicodeStringComponents {
|
||||
if let unicodeCodePoint = Int(unicodeStringComponent, radix: 16),
|
||||
let emojiUnicodeScalar = UnicodeScalar(unicodeCodePoint) {
|
||||
emoji.append(String(emojiUnicodeScalar))
|
||||
} else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .codepoint, in: container, debugDescription: "Cannot initialize emoji")
|
||||
}
|
||||
}
|
||||
|
||||
let name = try container.decode(String.self, forKey: .name)
|
||||
|
||||
let shortNames: [String]
|
||||
|
||||
if let decodedShortNames = try container.decodeIfPresent([String].self, forKey: .shortNames) {
|
||||
shortNames = decodedShortNames
|
||||
} else {
|
||||
shortNames = []
|
||||
}
|
||||
|
||||
let keywords: [String]
|
||||
|
||||
if let decodedKeywords = try container.decodeIfPresent([String].self, forKey: .keywords) {
|
||||
keywords = decodedKeywords
|
||||
} else {
|
||||
keywords = []
|
||||
}
|
||||
|
||||
self.init(shortName: shortName,
|
||||
value: emoji,
|
||||
name: name,
|
||||
shortNames: shortNames,
|
||||
keywords: keywords)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiMartStore {
|
||||
let categories: [EmojiJSONCategory]
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiMartStore: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiJSONStore properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case categories
|
||||
case emojis
|
||||
}
|
||||
|
||||
/// JSON key associated to emoji short name.
|
||||
struct EmojiKey: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
var intValue: Int? { return nil }
|
||||
init?(intValue: Int) { return nil }
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let emojisContainer = try container.nestedContainer(keyedBy: EmojiKey.self, forKey: .emojis)
|
||||
|
||||
let emojis: [EmojiItem] = emojisContainer.allKeys.compactMap { (emojiKey) -> EmojiItem? in
|
||||
let emojiItem: EmojiItem?
|
||||
|
||||
do {
|
||||
emojiItem = try emojisContainer.decode(EmojiItem.self, forKey: emojiKey)
|
||||
} catch {
|
||||
print("[EmojiJSONStore] init(from decoder: Decoder) failed to parse emojiItem \(emojiKey) with error: \(error)")
|
||||
emojiItem = nil
|
||||
}
|
||||
|
||||
return emojiItem
|
||||
}
|
||||
|
||||
let categories = try container.decode([EmojiJSONCategory].self, forKey: .categories)
|
||||
|
||||
self.init(categories: categories, emojis: emojis)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiMartCategory {
|
||||
|
||||
/// Emoji category identifier (e.g. "people")
|
||||
let identifier: String
|
||||
|
||||
/// Emoji category name in english (e.g. "Smiley & People")
|
||||
let name: String
|
||||
|
||||
/// List of emoji short names associated to the category (e.g. "people")
|
||||
let emojiShortNames: [String]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiMartCategory: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiJSONCategory properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case identifier = "id"
|
||||
case name
|
||||
case emojiShortNames = "emojis"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
enum EmojiServiceError: Error {
|
||||
case emojiJSONFileNotFound
|
||||
}
|
||||
|
||||
/// Emoji service powered by Emoji Mart data (https://github.com/missive/emoji-mart/)
|
||||
final class EmojiMartService: EmojiServiceType {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
/// Emoji data coming from https://github.com/missive/emoji-mart/blob/master/data/apple.json
|
||||
private static let jsonFilename = "apple_emojis_data"
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let serializationService: SerializationServiceType = SerializationService()
|
||||
private let serviceQueue = DispatchQueue(label: "\(type(of: EmojiMartService.self))")
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func getEmojiCategories(completion: @escaping (MXResponse<[EmojiCategory]>) -> Void) {
|
||||
self.serviceQueue.async {
|
||||
do {
|
||||
let emojiJSONData = try self.getEmojisJSONData()
|
||||
let emojiJSONStore: EmojiMartStore = try self.serializationService.deserialize(emojiJSONData)
|
||||
let emojiCategories = self.emojiCategories(from: emojiJSONStore)
|
||||
completion(MXResponse.success(emojiCategories))
|
||||
} catch {
|
||||
completion(MXResponse.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func getEmojisJSONData() throws -> Data {
|
||||
guard let jsonDataURL = Bundle.main.url(forResource: EmojiMartService.jsonFilename, withExtension: "json") else {
|
||||
throw EmojiServiceError.emojiJSONFileNotFound
|
||||
}
|
||||
let jsonData = try Data(contentsOf: jsonDataURL)
|
||||
return jsonData
|
||||
}
|
||||
|
||||
private func emojiCategories(from emojiJSONStore: EmojiMartStore) -> [EmojiCategory] {
|
||||
let allEmojiItems = emojiJSONStore.emojis
|
||||
|
||||
return emojiJSONStore.categories.map { (jsonCategory) -> EmojiCategory in
|
||||
let emojiItems = jsonCategory.emojiShortNames.compactMap({ (shortName) -> EmojiItem? in
|
||||
return allEmojiItems.first(where: { $0.shortName == shortName })
|
||||
})
|
||||
return EmojiCategory(identifier: jsonCategory.identifier, emojis: emojiItems)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiMartStore {
|
||||
let categories: [EmojiMartCategory]
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiMartStore: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiMartStore properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case categories
|
||||
case emojis
|
||||
}
|
||||
|
||||
/// JSON key associated to emoji short name.
|
||||
struct EmojiKey: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
var intValue: Int? { return nil }
|
||||
init?(intValue: Int) { return nil }
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let emojisContainer = try container.nestedContainer(keyedBy: EmojiKey.self, forKey: .emojis)
|
||||
|
||||
let emojis: [EmojiItem] = emojisContainer.allKeys.compactMap { (emojiKey) -> EmojiItem? in
|
||||
let emojiItem: EmojiItem?
|
||||
|
||||
do {
|
||||
emojiItem = try emojisContainer.decode(EmojiItem.self, forKey: emojiKey)
|
||||
} catch {
|
||||
print("[EmojiJSONStore] init(from decoder: Decoder) failed to parse emojiItem \(emojiKey) with error: \(error)")
|
||||
emojiItem = nil
|
||||
}
|
||||
|
||||
return emojiItem
|
||||
}
|
||||
|
||||
let categories = try container.decode([EmojiMartCategory].self, forKey: .categories)
|
||||
|
||||
self.init(categories: categories, emojis: emojis)
|
||||
}
|
||||
}
|
21
Riot/Modules/Room/EmojiPicker/Data/EmojiServiceType.swift
Normal file
21
Riot/Modules/Room/EmojiPicker/Data/EmojiServiceType.swift
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol EmojiServiceType {
|
||||
func getEmojiCategories(completion: @escaping (MXResponse<[EmojiCategory]>) -> Void)
|
||||
}
|
32
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiCategory.swift
Normal file
32
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiCategory.swift
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiCategory {
|
||||
|
||||
/// Emoji category identifier (e.g. "people")
|
||||
let identifier: String
|
||||
|
||||
/// Emoji list associated to category
|
||||
let emojis: [EmojiItem]
|
||||
|
||||
/// Emoji category localized name
|
||||
var name: String {
|
||||
let categoryNameLocalizationKey = "emoji_picker_\(self.identifier)_category"
|
||||
return VectorL10n.tr("Vector", categoryNameLocalizationKey)
|
||||
}
|
||||
}
|
57
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiItem.swift
Normal file
57
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiItem.swift
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiItem {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The commonly-agreed short name for the emoji, as supported in GitHub and others via the :short_name: syntax (e.g. "grinning" for 😀).
|
||||
let shortName: String
|
||||
|
||||
/// The emoji string (e.g. 😀)
|
||||
let value: String
|
||||
|
||||
/// The offical Unicode name (e.g. "Grinning Face" for 😀)
|
||||
let name: String
|
||||
|
||||
/// An array of all the other known short names (e.g. ["running"] for 🏃♂️).
|
||||
let shortNames: [String]
|
||||
|
||||
/// Associated emoji keywords (e.g. ["face","smile","happy"] for 😀).
|
||||
let keywords: [String]
|
||||
|
||||
/// For emoji with multiple skin tone variations, a list of alternative emoji items.
|
||||
let variations: [EmojiItem]
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(shortName: String,
|
||||
value: String,
|
||||
name: String,
|
||||
shortNames: [String] = [],
|
||||
keywords: [String] = [],
|
||||
variations: [EmojiItem] = []) {
|
||||
|
||||
self.shortName = shortName
|
||||
self.value = value
|
||||
self.name = name
|
||||
self.shortNames = shortNames
|
||||
self.keywords = keywords
|
||||
self.variations = variations
|
||||
}
|
||||
}
|
69
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiStore.swift
Normal file
69
Riot/Modules/Room/EmojiPicker/Data/Store/EmojiStore.swift
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
final class EmojiStore {
|
||||
|
||||
static let shared = EmojiStore()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var emojiCategories: [EmojiCategory] = []
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func getAll() -> [EmojiCategory] {
|
||||
return self.emojiCategories
|
||||
}
|
||||
|
||||
func set(_ emojiCategories: [EmojiCategory]) {
|
||||
self.emojiCategories = emojiCategories
|
||||
}
|
||||
|
||||
func findEmojiItemsSortedByCategory(with searchText: String) -> [EmojiCategory] {
|
||||
let initial: [EmojiCategory] = []
|
||||
|
||||
let filteredEmojiCategories = emojiCategories.reduce(into: initial) { (filteredEmojiCategories, emojiCategory) in
|
||||
|
||||
let filteredEmojiItems = emojiCategory.emojis.filter({ (emojiItem) -> Bool in
|
||||
|
||||
// Do not use `String.localizedCaseInsensitiveContains` here as EmojiItem data is not localized for the moment
|
||||
|
||||
if emojiItem.name.vc_caseInsensitiveContains(searchText) {
|
||||
return true
|
||||
}
|
||||
|
||||
if emojiItem.keywords.contains(where: { $0.vc_caseInsensitiveContains(searchText) }) {
|
||||
return true
|
||||
}
|
||||
|
||||
let shortNamesMatch = emojiItem.shortNames.contains { text -> Bool in
|
||||
return text.vc_caseInsensitiveContains(searchText)
|
||||
}
|
||||
|
||||
return shortNamesMatch
|
||||
})
|
||||
|
||||
if filteredEmojiItems.isEmpty == false {
|
||||
let filteredEmojiCategory = EmojiCategory(identifier: emojiCategory.identifier, emojis: filteredEmojiItems)
|
||||
filteredEmojiCategories.append(filteredEmojiCategory)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredEmojiCategories
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiPickerCategoryViewData {
|
||||
let identifier: String
|
||||
let name: String
|
||||
let emojiViewDataList: [EmojiPickerItemViewData]
|
||||
}
|
76
Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinator.swift
Normal file
76
Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinator.swift
Normal file
|
@ -0,0 +1,76 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class EmojiPickerCoordinator: EmojiPickerCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private let router: NavigationRouter
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: EmojiPickerCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.router = NavigationRouter(navigationController: RiotNavigationController())
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let emojiPickerViewModel = EmojiPickerViewModel(session: self.session, roomId: self.roomId, eventId: self.eventId)
|
||||
let emojiPickerViewController = EmojiPickerViewController.instantiate(with: emojiPickerViewModel)
|
||||
emojiPickerViewModel.coordinatorDelegate = self
|
||||
self.router.setRootModule(emojiPickerViewController)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.router.toPresentable()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - EmojiPickerViewModelCoordinatorDelegate
|
||||
extension EmojiPickerCoordinator: EmojiPickerViewModelCoordinatorDelegate {
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didAddEmoji emoji: String, forEventId eventId: String) {
|
||||
self.delegate?.emojiPickerCoordinator(self, didAddEmoji: emoji, forEventId: eventId)
|
||||
}
|
||||
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didRemoveEmoji emoji: String, forEventId eventId: String) {
|
||||
self.delegate?.emojiPickerCoordinator(self, didRemoveEmoji: emoji, forEventId: eventId)
|
||||
}
|
||||
|
||||
func emojiPickerViewModelDidCancel(_ viewModel: EmojiPickerViewModelType) {
|
||||
self.delegate?.emojiPickerCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc protocol EmojiPickerCoordinatorBridgePresenterDelegate {
|
||||
func emojiPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: EmojiPickerCoordinatorBridgePresenter, didAddEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: EmojiPickerCoordinatorBridgePresenter, didRemoveEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: EmojiPickerCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// EmojiPickerCoordinatorBridgePresenter enables to start EmojiPickerCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class EmojiPickerCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private var coordinator: EmojiPickerCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: EmojiPickerCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController,
|
||||
sourceView: UIView?,
|
||||
sourceRect: CGRect,
|
||||
animated: Bool) {
|
||||
let emojiPickerCoordinator = EmojiPickerCoordinator(session: self.session, roomId: self.roomId, eventId: self.eventId)
|
||||
emojiPickerCoordinator.delegate = self
|
||||
|
||||
let emojiPickerPresentable = emojiPickerCoordinator.toPresentable()
|
||||
|
||||
if let sourceView = sourceView {
|
||||
|
||||
emojiPickerPresentable.modalPresentationStyle = .popover
|
||||
|
||||
if let popoverPresentationController = emojiPickerPresentable.popoverPresentationController {
|
||||
popoverPresentationController.sourceView = sourceView
|
||||
|
||||
let finalSourceRect: CGRect
|
||||
|
||||
if sourceRect != CGRect.null {
|
||||
finalSourceRect = sourceRect
|
||||
} else {
|
||||
finalSourceRect = sourceView.bounds
|
||||
}
|
||||
|
||||
popoverPresentationController.sourceRect = finalSourceRect
|
||||
}
|
||||
}
|
||||
|
||||
viewController.present(emojiPickerPresentable, animated: animated, completion: nil)
|
||||
|
||||
emojiPickerCoordinator.start()
|
||||
|
||||
self.coordinator = emojiPickerCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - EmojiPickerCoordinatorDelegate
|
||||
extension EmojiPickerCoordinatorBridgePresenter: EmojiPickerCoordinatorDelegate {
|
||||
|
||||
func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didAddEmoji emoji: String, forEventId eventId: String) {
|
||||
self.delegate?.emojiPickerCoordinatorBridgePresenter(self, didAddEmoji: emoji, forEventId: eventId)
|
||||
}
|
||||
|
||||
func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didRemoveEmoji emoji: String, forEventId eventId: String) {
|
||||
self.delegate?.emojiPickerCoordinatorBridgePresenter(self, didRemoveEmoji: emoji, forEventId: eventId)
|
||||
}
|
||||
|
||||
func emojiPickerCoordinatorDidCancel(_ coordinator: EmojiPickerCoordinatorType) {
|
||||
self.delegate?.emojiPickerCoordinatorBridgePresenterDidCancel(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol EmojiPickerCoordinatorDelegate: class {
|
||||
func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didAddEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didRemoveEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerCoordinatorDidCancel(_ coordinator: EmojiPickerCoordinatorType)
|
||||
}
|
||||
|
||||
/// `EmojiPickerCoordinatorType` is a protocol describing a Coordinator that handle emoji picker navigation flow.
|
||||
protocol EmojiPickerCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: EmojiPickerCoordinatorDelegate? { get }
|
||||
}
|
36
Riot/Modules/Room/EmojiPicker/EmojiPickerHeaderView.swift
Normal file
36
Riot/Modules/Room/EmojiPicker/EmojiPickerHeaderView.swift
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class EmojiPickerHeaderView: UICollectionReusableView, NibReusable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.titleLabel.textColor = theme.headerTextPrimaryColor
|
||||
}
|
||||
|
||||
func fill(with title: String) {
|
||||
titleLabel.text = title
|
||||
}
|
||||
}
|
39
Riot/Modules/Room/EmojiPicker/EmojiPickerHeaderView.xib
Normal file
39
Riot/Modules/Room/EmojiPicker/EmojiPickerHeaderView.xib
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?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"/>
|
||||
<collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="EmojiPickerHeaderView" id="U6b-Vx-4bR" customClass="EmojiPickerHeaderView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wrW-Gv-YJV">
|
||||
<rect key="frame" x="20" y="8" width="280" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="wrW-Gv-YJV" secondAttribute="bottom" constant="8" id="D7a-Br-Vlz"/>
|
||||
<constraint firstItem="wrW-Gv-YJV" firstAttribute="top" secondItem="U6b-Vx-4bR" secondAttribute="top" constant="8" id="IGK-ta-CnU"/>
|
||||
<constraint firstItem="wrW-Gv-YJV" firstAttribute="leading" secondItem="VXr-Tz-HHm" secondAttribute="leading" constant="20" id="T3V-4C-3Qu"/>
|
||||
<constraint firstItem="VXr-Tz-HHm" firstAttribute="trailing" secondItem="wrW-Gv-YJV" secondAttribute="trailing" constant="20" id="wSZ-15-Co2"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="VXr-Tz-HHm"/>
|
||||
<connections>
|
||||
<outlet property="titleLabel" destination="wrW-Gv-YJV" id="wYw-xg-hha"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-404" y="-81"/>
|
||||
</collectionReusableView>
|
||||
</objects>
|
||||
</document>
|
23
Riot/Modules/Room/EmojiPicker/EmojiPickerItemViewData.swift
Normal file
23
Riot/Modules/Room/EmojiPicker/EmojiPickerItemViewData.swift
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct EmojiPickerItemViewData {
|
||||
let identifier: String
|
||||
let emoji: String
|
||||
let isSelected: Bool
|
||||
}
|
27
Riot/Modules/Room/EmojiPicker/EmojiPickerViewAction.swift
Normal file
27
Riot/Modules/Room/EmojiPicker/EmojiPickerViewAction.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
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
|
||||
|
||||
/// EmojiPickerViewController view actions exposed to view model
|
||||
enum EmojiPickerViewAction {
|
||||
case loadData
|
||||
case cancel
|
||||
case tap(emojiItemViewData: EmojiPickerItemViewData)
|
||||
case search(text: String?)
|
||||
}
|
101
Riot/Modules/Room/EmojiPicker/EmojiPickerViewCell.swift
Normal file
101
Riot/Modules/Room/EmojiPicker/EmojiPickerViewCell.swift
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class EmojiPickerViewCell: UICollectionViewCell, NibReusable, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let selectedBorderWidth: CGFloat = 1.0
|
||||
static let selectedBackgroundRadius: CGFloat = 5.0
|
||||
static let emojiHighlightedAlpha: CGFloat = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var reactionBackgroundView: UIView!
|
||||
@IBOutlet private weak var emojiLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
private var isReactionSelected: Bool = false
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
self.emojiLabel.alpha = isHighlighted ? Constants.emojiHighlightedAlpha : 1.0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
|
||||
self.reactionBackgroundView.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.reactionBackgroundView.layer.cornerRadius = self.reactionBackgroundView.frame.width/4.0
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(viewData: EmojiPickerItemViewData) {
|
||||
self.emojiLabel.text = viewData.emoji
|
||||
self.isReactionSelected = viewData.isSelected
|
||||
|
||||
self.updateViews()
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.reactionBackgroundView.layer.borderColor = theme.tintColor.cgColor
|
||||
self.emojiLabel.textColor = theme.textPrimaryColor
|
||||
self.updateViews()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateViews() {
|
||||
|
||||
let reactionBackgroundColor: UIColor?
|
||||
let reactionBackgroundBorderWidth: CGFloat
|
||||
|
||||
if self.isReactionSelected {
|
||||
reactionBackgroundColor = self.theme?.tintBackgroundColor
|
||||
reactionBackgroundBorderWidth = Constants.selectedBorderWidth
|
||||
} else {
|
||||
reactionBackgroundColor = self.theme?.headerBackgroundColor
|
||||
reactionBackgroundBorderWidth = 0.0
|
||||
}
|
||||
|
||||
self.reactionBackgroundView.layer.borderWidth = reactionBackgroundBorderWidth
|
||||
self.reactionBackgroundView.backgroundColor = reactionBackgroundColor
|
||||
}
|
||||
|
||||
}
|
55
Riot/Modules/Room/EmojiPicker/EmojiPickerViewCell.xib
Normal file
55
Riot/Modules/Room/EmojiPicker/EmojiPickerViewCell.xib
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?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"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="EmojiPickerViewCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Sam-a3-I0s">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="👍" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9GU-d6-Cnv">
|
||||
<rect key="frame" x="6" y="4" width="38" height="42"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.95294117649999999" green="0.97254901959999995" blue="0.99215686270000003" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="9GU-d6-Cnv" firstAttribute="top" secondItem="Sam-a3-I0s" secondAttribute="top" constant="4" id="T08-V8-wu1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="9GU-d6-Cnv" secondAttribute="bottom" constant="4" id="dgR-Un-bnz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9GU-d6-Cnv" secondAttribute="trailing" constant="6" id="gnQ-6e-N8Y"/>
|
||||
<constraint firstItem="9GU-d6-Cnv" firstAttribute="leading" secondItem="Sam-a3-I0s" secondAttribute="leading" constant="6" id="wHy-AN-9GI"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Sam-a3-I0s" secondAttribute="trailing" id="5FI-St-rx2"/>
|
||||
<constraint firstItem="Sam-a3-I0s" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="Oge-ZF-LX9"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Sam-a3-I0s" secondAttribute="bottom" id="f0f-GW-KI4"/>
|
||||
<constraint firstItem="Sam-a3-I0s" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="vze-ak-cqC"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
||||
<connections>
|
||||
<outlet property="emojiLabel" destination="9GU-d6-Cnv" id="5kc-lg-TpL"/>
|
||||
<outlet property="reactionBackgroundView" destination="Sam-a3-I0s" id="x8I-E5-BRG"/>
|
||||
</connections>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
</document>
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Emoji Picker View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="EmojiPickerViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Kjj-O1-UsZ">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="iPs-P1-bkS">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="V8j-Lb-PgC" id="ZkG-A0-JGC"/>
|
||||
<outlet property="delegate" destination="V8j-Lb-PgC" id="iXC-RG-INF"/>
|
||||
</connections>
|
||||
</collectionView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Kjj-O1-UsZ" firstAttribute="leading" secondItem="EL9-GA-lwo" secondAttribute="leading" id="6Ys-O9-EZy"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="Kjj-O1-UsZ" secondAttribute="top" id="MQy-0K-KQf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Kjj-O1-UsZ" secondAttribute="trailing" id="XkY-kj-afK"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Kjj-O1-UsZ" secondAttribute="bottom" id="cdk-7X-2Pn"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<extendedEdge key="edgesForExtendedLayout" bottom="YES"/>
|
||||
<connections>
|
||||
<outlet property="collectionView" destination="Kjj-O1-UsZ" id="iBb-J9-0Uc"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3199.1999999999998" y="-647.22638680659679"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
319
Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift
Normal file
319
Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift
Normal file
|
@ -0,0 +1,319 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class EmojiPickerViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum CollectionViewLayout {
|
||||
static let sectionInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
|
||||
static let minimumInteritemSpacing: CGFloat = 6.0
|
||||
static let minimumLineSpacing: CGFloat = 2.0
|
||||
static let itemSize = CGSize(width: 50, height: 50)
|
||||
}
|
||||
|
||||
private static let sizingHeaderView = EmojiPickerHeaderView.loadFromNib()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var collectionView: UICollectionView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: EmojiPickerViewModelType!
|
||||
private var theme: Theme!
|
||||
private var keyboardAvoider: KeyboardAvoider?
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private var searchController: UISearchController?
|
||||
private var emojiCategories: [EmojiPickerCategoryViewData] = []
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: EmojiPickerViewModelType) -> EmojiPickerViewController {
|
||||
let viewController = StoryboardScene.EmojiPickerViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.emojiPickerTitle
|
||||
|
||||
self.setupViews()
|
||||
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.collectionView)
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.keyboardAvoider?.startAvoiding()
|
||||
|
||||
// Update theme here otherwise the UISearchBar search text color is not updated
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
// Enable to hide search bar on scrolling after first time view appear
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.hidesSearchBarWhenScrolling = true
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.keyboardAvoider?.stopAvoiding()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
if let searchController = self.searchController {
|
||||
theme.applyStyle(onSearchBar: searchController.searchBar)
|
||||
}
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
self.setupCollectionView()
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
self.setupSearchController()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupCollectionView() {
|
||||
self.collectionView.delegate = self
|
||||
self.collectionView.dataSource = self
|
||||
|
||||
self.collectionView.keyboardDismissMode = .interactive
|
||||
|
||||
if let collectionViewFlowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
|
||||
collectionViewFlowLayout.minimumInteritemSpacing = CollectionViewLayout.minimumInteritemSpacing
|
||||
collectionViewFlowLayout.minimumLineSpacing = CollectionViewLayout.minimumLineSpacing
|
||||
collectionViewFlowLayout.itemSize = CollectionViewLayout.itemSize
|
||||
collectionViewFlowLayout.sectionInset = CollectionViewLayout.sectionInsets
|
||||
collectionViewFlowLayout.sectionHeadersPinToVisibleBounds = true // Enable sticky headers
|
||||
|
||||
// Avoid device notch in landascape (e.g. iPhone X)
|
||||
if #available(iOS 11.0, *) {
|
||||
collectionViewFlowLayout.sectionInsetReference = .fromSafeArea
|
||||
}
|
||||
}
|
||||
|
||||
self.collectionView.register(supplementaryViewType: EmojiPickerHeaderView.self, ofKind: UICollectionView.elementKindSectionHeader)
|
||||
self.collectionView.register(cellType: EmojiPickerViewCell.self)
|
||||
}
|
||||
|
||||
private func setupSearchController() {
|
||||
let searchController = UISearchController(searchResultsController: nil)
|
||||
searchController.dimsBackgroundDuringPresentation = false
|
||||
searchController.searchResultsUpdater = self
|
||||
searchController.searchBar.placeholder = VectorL10n.searchDefaultPlaceholder
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
self.navigationItem.searchController = searchController
|
||||
// Make the search bar visible on first view appearance
|
||||
self.navigationItem.hidesSearchBarWhenScrolling = false
|
||||
}
|
||||
|
||||
self.definesPresentationContext = true
|
||||
|
||||
self.searchController = searchController
|
||||
}
|
||||
|
||||
private func render(viewState: EmojiPickerViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(emojiCategories: let emojiCategories):
|
||||
self.renderLoaded(emojiCategories: emojiCategories)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(emojiCategories: [EmojiPickerCategoryViewData]) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.update(emojiCategories: emojiCategories)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func update(emojiCategories: [EmojiPickerCategoryViewData]) {
|
||||
self.emojiCategories = emojiCategories
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
|
||||
private func emojiItemViewData(at indexPath: IndexPath) -> EmojiPickerItemViewData {
|
||||
return self.emojiCategories[indexPath.section].emojiViewDataList[indexPath.row]
|
||||
}
|
||||
|
||||
private func emojiCategoryViewData(at section: Int) -> EmojiPickerCategoryViewData? {
|
||||
return self.emojiCategories[section]
|
||||
}
|
||||
|
||||
private func headerViewSize(for title: String) -> CGSize {
|
||||
|
||||
let sizingHeaderView = EmojiPickerViewController.sizingHeaderView
|
||||
|
||||
sizingHeaderView.fill(with: title)
|
||||
sizingHeaderView.setNeedsLayout()
|
||||
sizingHeaderView.layoutIfNeeded()
|
||||
|
||||
var fittingSize = UIView.layoutFittingCompressedSize
|
||||
fittingSize.width = self.collectionView.bounds.size.width
|
||||
|
||||
return sizingHeaderView.systemLayoutSizeFitting(fittingSize)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
extension EmojiPickerViewController: UICollectionViewDataSource {
|
||||
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
return self.emojiCategories.count
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return self.emojiCategories[section].emojiViewDataList.count
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
let emojiPickerCategory = self.emojiCategories[indexPath.section]
|
||||
|
||||
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, for: indexPath) as EmojiPickerHeaderView
|
||||
headerView.update(theme: self.theme)
|
||||
headerView.fill(with: emojiPickerCategory.name)
|
||||
|
||||
return headerView
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell: EmojiPickerViewCell = collectionView.dequeueReusableCell(for: indexPath)
|
||||
|
||||
if let theme = self.theme {
|
||||
cell.update(theme: theme)
|
||||
}
|
||||
|
||||
let viewData = self.emojiItemViewData(at: indexPath)
|
||||
cell.fill(viewData: viewData)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
extension EmojiPickerViewController: UICollectionViewDelegate {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let emojiItemViewData = self.emojiItemViewData(at: indexPath)
|
||||
self.viewModel.process(viewAction: .tap(emojiItemViewData: emojiItemViewData))
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {
|
||||
// Fix UICollectionView scroll bar appears underneath header view
|
||||
view.layer.zPosition = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegateFlowLayout
|
||||
extension EmojiPickerViewController: UICollectionViewDelegateFlowLayout {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
|
||||
let emojiCategory = self.emojiCategories[section]
|
||||
let headerSize = self.headerViewSize(for: emojiCategory.name)
|
||||
return headerSize
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - EmojiPickerViewModelViewDelegate
|
||||
extension EmojiPickerViewController: EmojiPickerViewModelViewDelegate {
|
||||
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didUpdateViewState viewSate: EmojiPickerViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISearchResultsUpdating
|
||||
extension EmojiPickerViewController: UISearchResultsUpdating {
|
||||
func updateSearchResults(for searchController: UISearchController) {
|
||||
let searchText = searchController.searchBar.text
|
||||
self.viewModel.process(viewAction: .search(text: searchText))
|
||||
}
|
||||
}
|
153
Riot/Modules/Room/EmojiPicker/EmojiPickerViewModel.swift
Normal file
153
Riot/Modules/Room/EmojiPicker/EmojiPickerViewModel.swift
Normal file
|
@ -0,0 +1,153 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
final class EmojiPickerViewModel: EmojiPickerViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private let emojiService: EmojiServiceType
|
||||
private let emojiStore: EmojiStore
|
||||
private let processingQueue: DispatchQueue
|
||||
|
||||
private lazy var aggregatedReactionsByEmoji: [String: MXReactionCount] = {
|
||||
return self.buildAggregatedReactionsByEmoji()
|
||||
}()
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: EmojiPickerViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: EmojiPickerViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.emojiService = EmojiMartService()
|
||||
self.emojiStore = EmojiStore.shared
|
||||
self.processingQueue = DispatchQueue(label: "\(type(of: self))")
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: EmojiPickerViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .tap(emojiItemViewData: let emojiItemViewData):
|
||||
let emoji = emojiItemViewData.emoji
|
||||
|
||||
if emojiItemViewData.isSelected {
|
||||
self.coordinatorDelegate?.emojiPickerViewModel(self, didRemoveEmoji: emoji, forEventId: self.eventId)
|
||||
} else {
|
||||
self.coordinatorDelegate?.emojiPickerViewModel(self, didAddEmoji: emoji, forEventId: self.eventId)
|
||||
}
|
||||
case .search(text: let searchText):
|
||||
self.searchEmojis(with: searchText)
|
||||
case .cancel:
|
||||
self.coordinatorDelegate?.emojiPickerViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
if self.emojiStore.getAll().isEmpty == false {
|
||||
let emojiCategories = self.emojiStore.getAll()
|
||||
let emojiCatagoryViewDataList = self.emojiCatagoryViewDataList(from: emojiCategories)
|
||||
self.update(viewState: .loaded(emojiCategories: emojiCatagoryViewDataList))
|
||||
} else {
|
||||
self.update(viewState: .loading)
|
||||
self.emojiService.getEmojiCategories { response in
|
||||
switch response {
|
||||
case .success(let emojiCategories):
|
||||
|
||||
self.emojiStore.set(emojiCategories)
|
||||
|
||||
let emojiCatagoryViewDataList = self.emojiCatagoryViewDataList(from: emojiCategories)
|
||||
DispatchQueue.main.async {
|
||||
self.update(viewState: .loaded(emojiCategories: emojiCatagoryViewDataList))
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func searchEmojis(with searchText: String?) {
|
||||
self.processingQueue.async {
|
||||
let filteredEmojiCategories: [EmojiCategory]
|
||||
|
||||
if let searchText = searchText, searchText.isEmpty == false {
|
||||
filteredEmojiCategories = self.emojiStore.findEmojiItemsSortedByCategory(with: searchText)
|
||||
} else {
|
||||
filteredEmojiCategories = self.emojiStore.getAll()
|
||||
}
|
||||
|
||||
let emojiCatagoryViewDataList = self.emojiCatagoryViewDataList(from: filteredEmojiCategories)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.update(viewState: .loaded(emojiCategories: emojiCatagoryViewDataList))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func update(viewState: EmojiPickerViewState) {
|
||||
self.viewDelegate?.emojiPickerViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func emojiCatagoryViewDataList(from emojiCategories: [EmojiCategory]) -> [EmojiPickerCategoryViewData] {
|
||||
return emojiCategories.map { (emojiCategory) -> EmojiPickerCategoryViewData in
|
||||
let emojiPickerViewDataList = emojiCategory.emojis.map({ (emojiItem) -> EmojiPickerItemViewData in
|
||||
let isSelected = self.isUserReacted(with: emojiItem.value)
|
||||
return EmojiPickerItemViewData(identifier: emojiItem.shortName, emoji: emojiItem.value, isSelected: isSelected)
|
||||
})
|
||||
return EmojiPickerCategoryViewData(identifier: emojiCategory.identifier, name: emojiCategory.name, emojiViewDataList: emojiPickerViewDataList)
|
||||
}
|
||||
}
|
||||
|
||||
private func isUserReacted(with emoji: String) -> Bool {
|
||||
guard let reactionCount = self.aggregatedReactionsByEmoji[emoji] else {
|
||||
return false
|
||||
}
|
||||
return reactionCount.myUserHasReacted
|
||||
}
|
||||
|
||||
private func buildAggregatedReactionsByEmoji() -> [String: MXReactionCount] {
|
||||
guard let aggregatedReactions = self.session.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId) else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
let initial: [String: MXReactionCount] = [:]
|
||||
|
||||
return aggregatedReactions.reactions.reduce(into: initial) { (aggregatedReactionsByEmoji, reactionCount) in
|
||||
aggregatedReactionsByEmoji[reactionCount.reaction] = reactionCount
|
||||
}
|
||||
}
|
||||
}
|
38
Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift
Normal file
38
Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift
Normal file
|
@ -0,0 +1,38 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol EmojiPickerViewModelViewDelegate: class {
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didUpdateViewState viewSate: EmojiPickerViewState)
|
||||
}
|
||||
|
||||
protocol EmojiPickerViewModelCoordinatorDelegate: class {
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didAddEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didRemoveEmoji emoji: String, forEventId eventId: String)
|
||||
func emojiPickerViewModelDidCancel(_ viewModel: EmojiPickerViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `EmojiPickerViewController`
|
||||
protocol EmojiPickerViewModelType {
|
||||
|
||||
var viewDelegate: EmojiPickerViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: EmojiPickerViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: EmojiPickerViewAction)
|
||||
}
|
26
Riot/Modules/Room/EmojiPicker/EmojiPickerViewState.swift
Normal file
26
Riot/Modules/Room/EmojiPicker/EmojiPickerViewState.swift
Normal file
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh toto EmojiPicker
|
||||
/*
|
||||
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
|
||||
|
||||
/// EmojiPickerViewController view state
|
||||
enum EmojiPickerViewState {
|
||||
case loading
|
||||
case loaded(emojiCategories: [EmojiPickerCategoryViewData])
|
||||
case error(Error)
|
||||
}
|
|
@ -1691,7 +1691,7 @@
|
|||
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
||||
|
||||
// text color
|
||||
UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"];
|
||||
UITextField *searchBarTextField = searchBar.vc_searchTextField;
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
|
||||
// Magnifying glass icon.
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc protocol ReactionHistoryCoordinatorBridgePresenterDelegate {
|
||||
func reactionHistoryCoordinatorBridgePresenterDelegateDidClose(_ coordinatorBridgePresenter: ReactionHistoryCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// ReactionHistoryCoordinatorBridgePresenter enables to start ReactionHistoryCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class ReactionHistoryCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private var coordinator: ReactionHistoryCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var isPresenting: Bool {
|
||||
return self.coordinator != nil
|
||||
}
|
||||
|
||||
weak var delegate: ReactionHistoryCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
|
||||
let reactionHistoryCoordinator = ReactionHistoryCoordinator(session: self.session, roomId: self.roomId, eventId: self.eventId)
|
||||
reactionHistoryCoordinator.delegate = self
|
||||
|
||||
let coordinatorPresentable = reactionHistoryCoordinator.toPresentable()
|
||||
coordinatorPresentable.modalPresentationStyle = .formSheet
|
||||
viewController.present(coordinatorPresentable, animated: animated, completion: nil)
|
||||
|
||||
reactionHistoryCoordinator.start()
|
||||
|
||||
self.coordinator = reactionHistoryCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReactionHistoryCoordinatorDelegate
|
||||
extension ReactionHistoryCoordinatorBridgePresenter: ReactionHistoryCoordinatorDelegate {
|
||||
func reactionHistoryCoordinatorDidClose(_ coordinator: ReactionHistoryCoordinatorType) {
|
||||
self.delegate?.reactionHistoryCoordinatorBridgePresenterDelegateDidClose(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class ReactionHistoryCoordinator: ReactionHistoryCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private let router: NavigationRouter
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: ReactionHistoryCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.router = NavigationRouter(navigationController: RiotNavigationController())
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let reactionHistoryViewModel = ReactionHistoryViewModel(session: session, roomId: roomId, eventId: eventId)
|
||||
let reactionHistoryViewController = ReactionHistoryViewController.instantiate(with: reactionHistoryViewModel)
|
||||
reactionHistoryViewModel.coordinatorDelegate = self
|
||||
self.router.setRootModule(reactionHistoryViewController)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.router.toPresentable()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReactionHistoryViewModelCoordinatorDelegate
|
||||
extension ReactionHistoryCoordinator: ReactionHistoryViewModelCoordinatorDelegate {
|
||||
func reactionHistoryViewModelDidClose(_ viewModel: ReactionHistoryViewModelType) {
|
||||
self.delegate?.reactionHistoryCoordinatorDidClose(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol ReactionHistoryCoordinatorDelegate: class {
|
||||
func reactionHistoryCoordinatorDidClose(_ coordinator: ReactionHistoryCoordinatorType)
|
||||
}
|
||||
|
||||
/// `ReactionHistoryCoordinatorType` is a protocol describing a Coordinator that handle reaction history navigation flow.
|
||||
protocol ReactionHistoryCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: ReactionHistoryCoordinatorDelegate? { get }
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
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
|
||||
|
||||
/// ReactionHistoryViewController view actions exposed to view model
|
||||
enum ReactionHistoryViewAction {
|
||||
case loadMore
|
||||
case close
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class ReactionHistoryViewCell: UITableViewCell, NibReusable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@IBOutlet private weak var reactionLabel: UILabel!
|
||||
@IBOutlet private weak var userDisplayNameLabel: UILabel!
|
||||
@IBOutlet private weak var timestampLabel: UILabel!
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(with viewData: ReactionHistoryViewData) {
|
||||
self.reactionLabel.text = viewData.reaction
|
||||
self.userDisplayNameLabel.text = viewData.userDisplayName
|
||||
self.timestampLabel.text = viewData.dateString
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.reactionLabel.textColor = theme.textPrimaryColor
|
||||
self.userDisplayNameLabel.textColor = theme.textPrimaryColor
|
||||
self.timestampLabel.textColor = theme.textSecondaryColor
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="46" id="KGk-i7-Jjw" customClass="ReactionHistoryViewCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="578" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="578" height="45.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" horizontalCompressionResistancePriority="752" text="😀" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgX-dJ-kCR">
|
||||
<rect key="frame" x="20" y="10" width="24" height="25.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="user name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vcy-eW-DCg">
|
||||
<rect key="frame" x="54" y="13" width="460.5" height="19.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="20:30" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Euk-Mr-DGp">
|
||||
<rect key="frame" x="524.5" y="15.5" width="33.5" height="14.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="sgX-dJ-kCR" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" priority="750" constant="10" id="1d4-Pd-ffp"/>
|
||||
<constraint firstItem="Vcy-eW-DCg" firstAttribute="leading" secondItem="sgX-dJ-kCR" secondAttribute="trailing" constant="10" id="2Se-Si-LeY"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Vcy-eW-DCg" secondAttribute="bottom" constant="10" id="AKa-IS-sS3"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sgX-dJ-kCR" secondAttribute="bottom" priority="750" constant="10" id="Bq1-yU-Eju"/>
|
||||
<constraint firstItem="sgX-dJ-kCR" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="Gnp-Pg-a5X"/>
|
||||
<constraint firstItem="Vcy-eW-DCg" firstAttribute="centerY" secondItem="sgX-dJ-kCR" secondAttribute="centerY" id="N2F-Pw-CRB"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="sgX-dJ-kCR" secondAttribute="bottom" constant="10" id="SQg-Iz-ZA6"/>
|
||||
<constraint firstItem="Euk-Mr-DGp" firstAttribute="leading" secondItem="Vcy-eW-DCg" secondAttribute="trailing" constant="10" id="W4s-gm-wXT"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Euk-Mr-DGp" secondAttribute="trailing" constant="20" id="c1y-WQ-DmA"/>
|
||||
<constraint firstItem="sgX-dJ-kCR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="k6s-g1-TzX"/>
|
||||
<constraint firstItem="Vcy-eW-DCg" firstAttribute="top" relation="greaterThanOrEqual" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="qRt-N3-3rj"/>
|
||||
<constraint firstItem="Euk-Mr-DGp" firstAttribute="centerY" secondItem="sgX-dJ-kCR" secondAttribute="centerY" id="zNz-zT-nFd"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<connections>
|
||||
<outlet property="reactionLabel" destination="sgX-dJ-kCR" id="gGv-pT-xAa"/>
|
||||
<outlet property="timestampLabel" destination="Euk-Mr-DGp" id="ZCp-Og-Tar"/>
|
||||
<outlet property="userDisplayNameLabel" destination="Vcy-eW-DCg" id="v95-in-PbT"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-71.014492753623188" y="79.017857142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Reaction History View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="ReactionHistoryViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="HkR-sa-Jjv">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="V8j-Lb-PgC" id="NeJ-vG-i82"/>
|
||||
<outlet property="delegate" destination="V8j-Lb-PgC" id="Ddg-k9-R6c"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="HkR-sa-Jjv" firstAttribute="top" secondItem="bFg-jh-JZB" secondAttribute="top" id="8aH-El-E6A"/>
|
||||
<constraint firstAttribute="trailing" secondItem="HkR-sa-Jjv" secondAttribute="trailing" id="L5p-CX-oPq"/>
|
||||
<constraint firstItem="HkR-sa-Jjv" firstAttribute="leading" secondItem="EL9-GA-lwo" secondAttribute="leading" id="Vo4-SY-4qi"/>
|
||||
<constraint firstAttribute="bottom" secondItem="HkR-sa-Jjv" secondAttribute="bottom" id="kdK-64-7jj"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="HkR-sa-Jjv" id="PHl-xo-AGs"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3198" y="-647"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -0,0 +1,206 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class ReactionHistoryViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum TableView {
|
||||
static let estimatedRowHeight: CGFloat = 21.0
|
||||
static let contentInset = UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: ReactionHistoryViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private var isViewAppearedOnce: Bool = false
|
||||
|
||||
private var reactionHistoryViewDataList: [ReactionHistoryViewData] = []
|
||||
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: ReactionHistoryViewModelType) -> ReactionHistoryViewController {
|
||||
let viewController = StoryboardScene.ReactionHistoryViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.reactionHistoryTitle
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.setupViews()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.process(viewAction: .loadMore)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if self.isViewAppearedOnce == false {
|
||||
self.isViewAppearedOnce = true
|
||||
}
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
self.tableView.backgroundColor = theme.backgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
}
|
||||
|
||||
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 setupTableView() {
|
||||
self.tableView.contentInset = TableView.contentInset
|
||||
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedRowHeight = TableView.estimatedRowHeight
|
||||
self.tableView.register(cellType: ReactionHistoryViewCell.self)
|
||||
|
||||
self.tableView.tableFooterView = UIView()
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let closeBarButtonItem = MXKBarButtonItem(title: VectorL10n.close, style: .plain) { [weak self] in
|
||||
self?.closeButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = closeBarButtonItem
|
||||
|
||||
self.setupTableView()
|
||||
}
|
||||
|
||||
private func render(viewState: ReactionHistoryViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(reactionHistoryViewDataList: let reactionHistoryViewDataList, allDataLoaded: let allDataLoaded):
|
||||
self.renderLoaded(reactionHistoryViewDataList: reactionHistoryViewDataList, allDataLoaded: allDataLoaded)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(reactionHistoryViewDataList: [ReactionHistoryViewData], allDataLoaded: Bool) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.reactionHistoryViewDataList = reactionHistoryViewDataList
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func closeButtonAction() {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension ReactionHistoryViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.reactionHistoryViewDataList.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let reactionHistoryCell = tableView.dequeueReusableCell(for: indexPath, cellType: ReactionHistoryViewCell.self)
|
||||
|
||||
let reactionHistoryViewData = self.reactionHistoryViewDataList[indexPath.row]
|
||||
|
||||
reactionHistoryCell.update(theme: self.theme)
|
||||
reactionHistoryCell.fill(with: reactionHistoryViewData)
|
||||
|
||||
return reactionHistoryCell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension ReactionHistoryViewController: UITableViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
||||
guard self.isViewAppearedOnce else {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if a scroll beyond scroll view content occurs
|
||||
let distanceFromBottom = scrollView.contentSize.height - scrollView.contentOffset.y
|
||||
if distanceFromBottom < scrollView.frame.size.height {
|
||||
self.viewModel.process(viewAction: .loadMore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReactionHistoryViewModelViewDelegate
|
||||
extension ReactionHistoryViewController: ReactionHistoryViewModelViewDelegate {
|
||||
|
||||
func reactionHistoryViewModel(_ viewModel: ReactionHistoryViewModelType, didUpdateViewState viewSate: ReactionHistoryViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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
|
||||
|
||||
struct ReactionHistoryViewData {
|
||||
let reaction: String
|
||||
let userDisplayName: String
|
||||
let dateString: String
|
||||
}
|
179
Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModel.swift
Normal file
179
Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModel.swift
Normal file
|
@ -0,0 +1,179 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
final class ReactionHistoryViewModel: ReactionHistoryViewModelType {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Pagination {
|
||||
static let count: UInt = 30
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let eventId: String
|
||||
private let aggregations: MXAggregations
|
||||
private let eventFormatter: MXKEventFormatter
|
||||
private let reactionsFormattingQueue: DispatchQueue
|
||||
|
||||
private var reactionHistoryViewDataList: [ReactionHistoryViewData] = []
|
||||
private var operation: MXHTTPOperation?
|
||||
private var nextBatch: String?
|
||||
private var viewState: ReactionHistoryViewState?
|
||||
|
||||
private lazy var roomMembers: MXRoomMembers? = {
|
||||
return buildRoomMembers()
|
||||
}()
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: ReactionHistoryViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: ReactionHistoryViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String, eventId: String) {
|
||||
self.session = session
|
||||
self.aggregations = session.aggregations
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.eventFormatter = EventFormatter(matrixSession: session)
|
||||
self.reactionsFormattingQueue = DispatchQueue(label: "\(type(of: self)).reactionsFormattingQueue")
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: ReactionHistoryViewAction) {
|
||||
switch viewAction {
|
||||
case .loadMore:
|
||||
self.loadMoreHistory()
|
||||
case .close:
|
||||
self.coordinatorDelegate?.reactionHistoryViewModelDidClose(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private func canLoadMoreHistory() -> Bool {
|
||||
guard let viewState = self.viewState else {
|
||||
return true
|
||||
}
|
||||
|
||||
let canLoadMoreHistory: Bool
|
||||
|
||||
switch viewState {
|
||||
case .loading:
|
||||
canLoadMoreHistory = false
|
||||
case .loaded(reactionHistoryViewDataList: _, allDataLoaded: let allDataLoaded):
|
||||
canLoadMoreHistory = !allDataLoaded
|
||||
default:
|
||||
canLoadMoreHistory = true
|
||||
}
|
||||
|
||||
return canLoadMoreHistory
|
||||
}
|
||||
|
||||
private func loadMoreHistory() {
|
||||
guard self.canLoadMoreHistory() else {
|
||||
print("[ReactionHistoryViewModel] loadMoreHistory: pending loading or all data loaded")
|
||||
return
|
||||
}
|
||||
|
||||
guard self.operation == nil else {
|
||||
print("[ReactionHistoryViewModel] loadMoreHistory: operation already pending")
|
||||
return
|
||||
}
|
||||
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.operation = self.aggregations.reactionsEvents(forEvent: self.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.nextBatch = response.nextBatch
|
||||
self.operation = nil
|
||||
|
||||
self.process(reactionEvents: response.chunk, nextBatch: response.nextBatch)
|
||||
|
||||
}, failure: { [weak self] error in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.operation = nil
|
||||
self.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func process(reactionEvents: [MXEvent], nextBatch: String?) {
|
||||
self.reactionsFormattingQueue.async {
|
||||
|
||||
let reactionHistoryList = reactionEvents.compactMap { (reactionEvent) -> ReactionHistoryViewData? in
|
||||
return self.reactionHistoryViewData(from: reactionEvent)
|
||||
}
|
||||
|
||||
self.reactionHistoryViewDataList.append(contentsOf: reactionHistoryList)
|
||||
|
||||
let allDataLoaded = nextBatch == nil
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.update(viewState: .loaded(reactionHistoryViewDataList: self.reactionHistoryViewDataList, allDataLoaded: allDataLoaded))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func reactionHistoryViewData(from reactionEvent: MXEvent) -> ReactionHistoryViewData? {
|
||||
guard let userId = reactionEvent.sender,
|
||||
let reaction = reactionEvent.relatesTo?.key,
|
||||
let reactionDateString = self.eventFormatter.dateString(fromTimestamp: reactionEvent.originServerTs, withTime: true) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let userDisplayName = self.userDisplayName(from: userId) ?? userId
|
||||
|
||||
return ReactionHistoryViewData(reaction: reaction, userDisplayName: userDisplayName, dateString: reactionDateString)
|
||||
}
|
||||
|
||||
private func userDisplayName(from userId: String) -> String? {
|
||||
guard let roomMembers = self.roomMembers else {
|
||||
return nil
|
||||
}
|
||||
let roomMember = roomMembers.member(withUserId: userId)
|
||||
return roomMember?.displayname
|
||||
}
|
||||
|
||||
private func buildRoomMembers() -> MXRoomMembers? {
|
||||
guard let room = self.session.room(withRoomId: self.roomId) else {
|
||||
return nil
|
||||
}
|
||||
return room.dangerousSyncState?.members
|
||||
}
|
||||
|
||||
private func update(viewState: ReactionHistoryViewState) {
|
||||
self.viewState = viewState
|
||||
self.viewDelegate?.reactionHistoryViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol ReactionHistoryViewModelViewDelegate: class {
|
||||
func reactionHistoryViewModel(_ viewModel: ReactionHistoryViewModelType, didUpdateViewState viewSate: ReactionHistoryViewState)
|
||||
}
|
||||
|
||||
protocol ReactionHistoryViewModelCoordinatorDelegate: class {
|
||||
func reactionHistoryViewModelDidClose(_ viewModel: ReactionHistoryViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `ReactionHistoryViewController`
|
||||
protocol ReactionHistoryViewModelType {
|
||||
|
||||
var viewDelegate: ReactionHistoryViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: ReactionHistoryViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: ReactionHistoryViewAction)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh ReactionHistory ReactionHistory
|
||||
/*
|
||||
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
|
||||
|
||||
/// ReactionHistoryViewController view state
|
||||
enum ReactionHistoryViewState {
|
||||
case loading
|
||||
case loaded(reactionHistoryViewDataList: [ReactionHistoryViewData], allDataLoaded: Bool)
|
||||
case error(Error)
|
||||
}
|
|
@ -124,7 +124,8 @@
|
|||
#import "Riot-Swift.h"
|
||||
|
||||
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
|
||||
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate>
|
||||
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate, EmojiPickerCoordinatorBridgePresenterDelegate,
|
||||
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
// The expanded header
|
||||
ExpandedRoomTitleView *expandedHeader;
|
||||
|
@ -223,6 +224,10 @@
|
|||
@property (nonatomic, strong) NSString *textMessageBeforeEditing;
|
||||
@property (nonatomic, strong) EditHistoryCoordinatorBridgePresenter *editHistoryPresenter;
|
||||
@property (nonatomic, strong) MXKDocumentPickerPresenter *documentPickerPresenter;
|
||||
@property (nonatomic, strong) EmojiPickerCoordinatorBridgePresenter *emojiPickerCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) ReactionHistoryCoordinatorBridgePresenter *reactionHistoryCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) CameraPresenter *cameraPresenter;
|
||||
@property (nonatomic, strong) MediaPickerCoordinatorBridgePresenter *mediaPickerPresenter;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -1530,6 +1535,54 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)showReactionHistoryForEventId:(NSString*)eventId animated:(BOOL)animated
|
||||
{
|
||||
if (self.reactionHistoryCoordinatorBridgePresenter.isPresenting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReactionHistoryCoordinatorBridgePresenter *presenter = [[ReactionHistoryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession roomId:self.roomDataSource.roomId eventId:eventId];
|
||||
presenter.delegate = self;
|
||||
|
||||
[presenter presentFrom:self animated:animated];
|
||||
|
||||
self.reactionHistoryCoordinatorBridgePresenter = presenter;
|
||||
}
|
||||
|
||||
- (void)showCameraControllerAnimated:(BOOL)animated
|
||||
{
|
||||
CameraPresenter *cameraPresenter = [CameraPresenter new];
|
||||
cameraPresenter.delegate = self;
|
||||
[cameraPresenter presentCameraFrom:self with:@[MXKUTI.image, MXKUTI.movie] animated:YES];
|
||||
|
||||
self.cameraPresenter = cameraPresenter;
|
||||
}
|
||||
|
||||
|
||||
- (void)showMediaPickerAnimated:(BOOL)animated
|
||||
{
|
||||
MediaPickerCoordinatorBridgePresenter *mediaPickerPresenter = [[MediaPickerCoordinatorBridgePresenter alloc] initWithSession:self.mainSession mediaUTIs:@[MXKUTI.image, MXKUTI.movie] allowsMultipleSelection:YES];
|
||||
mediaPickerPresenter.delegate = self;
|
||||
|
||||
UIView *sourceView;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
sourceView = roomInputToolbarView.attachMediaButton;
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceView = self.inputToolbarView;
|
||||
}
|
||||
|
||||
[mediaPickerPresenter presentFrom:self sourceView:sourceView sourceRect:sourceView.bounds animated:YES];
|
||||
|
||||
self.mediaPickerPresenter = mediaPickerPresenter;
|
||||
}
|
||||
|
||||
#pragma mark - Hide/Show expanded header
|
||||
|
||||
- (void)showExpandedHeader:(BOOL)isVisible
|
||||
|
@ -2172,6 +2225,14 @@
|
|||
[self handleLongPressFromCell:cell withTappedEvent:tappedEvent];
|
||||
}
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnReactionView])
|
||||
{
|
||||
NSString *tappedEventId = userInfo[kMXKRoomBubbleCellEventIdKey];
|
||||
if (tappedEventId)
|
||||
{
|
||||
[self showReactionHistoryForEventId:tappedEventId animated:YES];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep default implementation for other actions
|
||||
|
@ -2532,6 +2593,20 @@
|
|||
|
||||
}]];
|
||||
|
||||
// Add reaction history if event contains reactions
|
||||
if (roomBubbleTableViewCell.bubbleData.reactions[selectedEvent.eventId].aggregatedReactionsWithNonZeroCount)
|
||||
{
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_reaction_history", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
[self cancelEventSelection];
|
||||
|
||||
// Show reaction history
|
||||
[self showReactionHistoryForEventId:selectedEvent.eventId animated:YES];
|
||||
}]];
|
||||
}
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_source", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
@ -2710,19 +2785,9 @@
|
|||
// Do not display empty action sheet
|
||||
if (currentAlert.actions.count > 1)
|
||||
{
|
||||
NSArray *components = roomBubbleTableViewCell.bubbleData.bubbleComponents;
|
||||
NSInteger bubbleComponentIndex = [roomBubbleTableViewCell.bubbleData bubbleComponentIndexForEventId:selectedEvent.eventId];
|
||||
|
||||
NSInteger index = 0;
|
||||
for (MXKRoomBubbleComponent *component in components)
|
||||
{
|
||||
if ([component.event.eventId isEqualToString:selectedEvent.eventId])
|
||||
{
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
CGRect sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:index];
|
||||
CGRect sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:bubbleComponentIndex];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier:@"RoomVCEventMenuAlert"];
|
||||
[currentAlert popoverPresentationController].sourceView = roomBubbleTableViewCell;
|
||||
|
@ -3368,6 +3433,16 @@
|
|||
self.documentPickerPresenter = documentPickerPresenter;
|
||||
}
|
||||
|
||||
- (void)roomInputToolbarViewDidTapCamera:(MXKRoomInputToolbarView*)toolbarView
|
||||
{
|
||||
[self showCameraControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)roomInputToolbarViewDidTapMediaLibrary:(MXKRoomInputToolbarView*)toolbarView
|
||||
{
|
||||
[self showMediaPickerAnimated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - RoomParticipantsViewControllerDelegate
|
||||
|
||||
- (void)roomParticipantsViewController:(RoomParticipantsViewController *)roomParticipantsViewController mention:(MXRoomMember*)member
|
||||
|
@ -5200,15 +5275,7 @@
|
|||
MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData;
|
||||
NSArray *bubbleComponents = bubbleCellData.bubbleComponents;
|
||||
|
||||
NSInteger foundComponentIndex = [bubbleComponents indexOfObjectPassingTest:^BOOL(MXKRoomBubbleComponent * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (obj.event.eventId == selectedEventId)
|
||||
{
|
||||
*stop = YES;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
|
||||
NSInteger foundComponentIndex = [bubbleCellData bubbleComponentIndexForEventId:event.eventId];
|
||||
CGRect bubbleComponentFrame;
|
||||
|
||||
if (bubbleComponents.count > 0)
|
||||
|
@ -5334,6 +5401,37 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)reactionsMenuViewModelDidTapMoreReactions:(ReactionsMenuViewModel *)viewModel forEventId:(NSString *)eventId
|
||||
{
|
||||
[self hideContextualMenuAnimated:YES];
|
||||
|
||||
EmojiPickerCoordinatorBridgePresenter *emojiPickerCoordinatorBridgePresenter = [[EmojiPickerCoordinatorBridgePresenter alloc] initWithSession:self.mainSession roomId:self.roomDataSource.roomId eventId:eventId];
|
||||
emojiPickerCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
NSInteger cellRow = [self.roomDataSource indexOfCellDataWithEventId:eventId];
|
||||
|
||||
UIView *sourceView;
|
||||
CGRect sourceRect = CGRectNull;
|
||||
|
||||
if (cellRow >= 0)
|
||||
{
|
||||
NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:cellRow inSection:0];
|
||||
UITableViewCell *cell = [self.bubblesTableView cellForRowAtIndexPath:cellIndexPath];
|
||||
sourceView = cell;
|
||||
|
||||
if ([cell isKindOfClass:[MXKRoomBubbleTableViewCell class]])
|
||||
{
|
||||
MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell;
|
||||
NSInteger bubbleComponentIndex = [roomBubbleTableViewCell.bubbleData bubbleComponentIndexForEventId:eventId];
|
||||
sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:bubbleComponentIndex];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[emojiPickerCoordinatorBridgePresenter presentFrom:self sourceView:sourceView sourceRect:sourceRect animated:YES];
|
||||
self.emojiPickerCoordinatorBridgePresenter = emojiPickerCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)showEditHistoryForEventId:(NSString*)eventId animated:(BOOL)animated
|
||||
|
@ -5401,5 +5499,131 @@
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - EmojiPickerCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)emojiPickerCoordinatorBridgePresenter:(EmojiPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didAddEmoji:(NSString *)emoji forEventId:(NSString *)eventId
|
||||
{
|
||||
MXWeakify(self);
|
||||
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self.roomDataSource addReaction:emoji forEventId:eventId success:^{
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil];
|
||||
}];
|
||||
}];
|
||||
self.emojiPickerCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)emojiPickerCoordinatorBridgePresenter:(EmojiPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didRemoveEmoji:(NSString *)emoji forEventId:(NSString *)eventId
|
||||
{
|
||||
MXWeakify(self);
|
||||
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
|
||||
[self.roomDataSource removeReaction:emoji forEventId:eventId success:^{
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil];
|
||||
}];
|
||||
}];
|
||||
self.emojiPickerCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)emojiPickerCoordinatorBridgePresenterDidCancel:(EmojiPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.emojiPickerCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - ReactionHistoryCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)reactionHistoryCoordinatorBridgePresenterDelegateDidClose:(ReactionHistoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
self.reactionHistoryCoordinatorBridgePresenter = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - CameraPresenterDelegate
|
||||
|
||||
- (void)cameraPresenterDidCancel:(CameraPresenter *)cameraPresenter
|
||||
{
|
||||
[cameraPresenter dismissWithAnimated:YES completion:nil];
|
||||
self.cameraPresenter = nil;
|
||||
}
|
||||
|
||||
- (void)cameraPresenter:(CameraPresenter *)cameraPresenter didSelectImageData:(NSData *)imageData withUTI:(MXKUTI *)uti
|
||||
{
|
||||
[cameraPresenter dismissWithAnimated:YES completion:nil];
|
||||
self.cameraPresenter = nil;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
[roomInputToolbarView sendSelectedImage:imageData withMimeType:uti.mimeType andCompressionMode:MXKRoomInputToolbarCompressionModePrompt isPhotoLibraryAsset:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cameraPresenter:(CameraPresenter *)cameraPresenter didSelectVideoAt:(NSURL *)url
|
||||
{
|
||||
[cameraPresenter dismissWithAnimated:YES completion:nil];
|
||||
self.cameraPresenter = nil;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
[roomInputToolbarView sendSelectedVideo:url isPhotoLibraryAsset:NO];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MediaPickerCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)mediaPickerCoordinatorBridgePresenterDidCancel:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.mediaPickerPresenter = nil;
|
||||
}
|
||||
|
||||
- (void)mediaPickerCoordinatorBridgePresenter:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectImageData:(NSData *)imageData withUTI:(MXKUTI *)uti
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.mediaPickerPresenter = nil;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
[roomInputToolbarView sendSelectedImage:imageData withMimeType:uti.mimeType andCompressionMode:MXKRoomInputToolbarCompressionModePrompt isPhotoLibraryAsset:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mediaPickerCoordinatorBridgePresenter:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectVideoAt:(NSURL *)url
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.mediaPickerPresenter = nil;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
[roomInputToolbarView sendSelectedVideo:url isPhotoLibraryAsset:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mediaPickerCoordinatorBridgePresenter:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectAssets:(NSArray<PHAsset *> *)assets
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.mediaPickerPresenter = nil;
|
||||
|
||||
RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView];
|
||||
if (roomInputToolbarView)
|
||||
{
|
||||
[roomInputToolbarView sendSelectedAssets:assets withCompressionMode:MXKRoomInputToolbarCompressionModePrompt];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
} RoomSettingsViewControllerField;
|
||||
|
||||
@interface RoomSettingsViewController : MXKRoomSettingsViewController <UITextViewDelegate, UITextFieldDelegate, MediaPickerViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, TableViewCellWithCheckBoxesDelegate>
|
||||
@interface RoomSettingsViewController : MXKRoomSettingsViewController <UITextViewDelegate, UITextFieldDelegate, MXKRoomMemberDetailsViewControllerDelegate, TableViewCellWithCheckBoxesDelegate>
|
||||
|
||||
/**
|
||||
Select a settings field in order to edit it ('RoomSettingsViewControllerFieldNone' by default).
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue