mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
This commit is contained in:
parent
35cbc84a99
commit
7a47e37d38
@ -511,7 +511,6 @@
|
||||
71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; };
|
||||
733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */; };
|
||||
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */; };
|
||||
73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */; };
|
||||
73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E2E5103702D13361D09100 /* UserProfileScreenViewModelTests.swift */; };
|
||||
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; };
|
||||
@ -764,6 +763,7 @@
|
||||
A4E885358D7DD5A072A06824 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; };
|
||||
A51C65E5A3C9F2464A91A380 /* AuthenticationClientBuilderFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */; };
|
||||
A52090A4FE0DB826578DFC03 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0724EBDFE8BB4C9E5547C57D /* Client.swift */; };
|
||||
A5B455D1A6DADF7476F7B417 /* EmojiProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */; };
|
||||
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; };
|
||||
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; };
|
||||
A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; };
|
||||
@ -969,7 +969,6 @@
|
||||
D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */; };
|
||||
D5681C80D8281560AACE0035 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045253F9967A535EE5B16691 /* Label.swift */; };
|
||||
D5B1531A72387D432939D4E0 /* RoomDirectorySearchProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */; };
|
||||
D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */; };
|
||||
D5E771132BB36240DE38102F /* RoomMessageEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */; };
|
||||
D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; };
|
||||
D6152E21036B88C44ECB22E7 /* EncryptionResetPasswordScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303D9438EFB481F57A366E82 /* EncryptionResetPasswordScreenViewModel.swift */; };
|
||||
@ -1455,7 +1454,6 @@
|
||||
36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = "<group>"; };
|
||||
376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = "<group>"; };
|
||||
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
||||
37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringTests.swift; sourceTree = "<group>"; };
|
||||
37FEE10AB666891E6A675E5E /* SecureBackupLogoutConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreen.swift; sourceTree = "<group>"; };
|
||||
@ -1474,7 +1472,6 @@
|
||||
3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = "<group>"; };
|
||||
3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = "<group>"; };
|
||||
3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = "<group>"; };
|
||||
3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = "<group>"; };
|
||||
@ -1815,6 +1812,7 @@
|
||||
8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersStateTests.swift; sourceTree = "<group>"; };
|
||||
8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainMentionBuilder.swift; sourceTree = "<group>"; };
|
||||
8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoader.swift; sourceTree = "<group>"; };
|
||||
8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenModels.swift; sourceTree = "<group>"; };
|
||||
8C44BBC892499BE45B074F89 /* AppLockScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||
@ -3080,10 +3078,9 @@
|
||||
39557ADF21345E18F3865B9E /* Emojis */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */,
|
||||
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */,
|
||||
201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */,
|
||||
6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */,
|
||||
8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */,
|
||||
);
|
||||
path = Emojis;
|
||||
sourceTree = "<group>";
|
||||
@ -6448,9 +6445,7 @@
|
||||
370AF5BFCD4384DD455479B6 /* ElementCallWidgetDriverProtocol.swift in Sources */,
|
||||
3F997171C3C79A45E92BF9EF /* ElementWellKnown.swift in Sources */,
|
||||
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */,
|
||||
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */,
|
||||
E45C9FA22BC13B477FD3B4AC /* EmojiDetection.swift in Sources */,
|
||||
D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */,
|
||||
3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */,
|
||||
340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */,
|
||||
C1910A16BDF131FECA77BE22 /* EmojiPickerScreenCoordinator.swift in Sources */,
|
||||
@ -6459,6 +6454,7 @@
|
||||
2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */,
|
||||
1D69E31913DF66426985909B /* EmojiPickerScreenViewModelProtocol.swift in Sources */,
|
||||
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */,
|
||||
A5B455D1A6DADF7476F7B417 /* EmojiProviderProtocol.swift in Sources */,
|
||||
5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */,
|
||||
8B41D0357B91CD3B6F6A3BCA /* EmoteRoomTimelineItemContent.swift in Sources */,
|
||||
661EF50C1F7D4B0BC8A7AAE3 /* EmoteRoomTimelineView.swift in Sources */,
|
||||
|
@ -139,6 +139,7 @@
|
||||
"common_edited_suffix" = "(edited)";
|
||||
"common_editing" = "Editing";
|
||||
"common_emote" = "* %1$@ %2$@";
|
||||
"common_encryption" = "Encryption";
|
||||
"common_encryption_enabled" = "Encryption enabled";
|
||||
"common_enter_your_pin" = "Enter your PIN";
|
||||
"common_error" = "Error";
|
||||
@ -149,6 +150,7 @@
|
||||
"common_favourited" = "Favourited";
|
||||
"common_file" = "File";
|
||||
"common_forward_message" = "Forward message";
|
||||
"common_frequently_used" = "Frequently used";
|
||||
"common_gif" = "GIF";
|
||||
"common_image" = "Image";
|
||||
"common_in_reply_to" = "In reply to %1$@";
|
||||
@ -342,6 +344,9 @@
|
||||
"screen_advanced_settings_element_call_base_url" = "Custom Element Call base URL";
|
||||
"screen_advanced_settings_element_call_base_url_description" = "Set a custom base URL for Element Call.";
|
||||
"screen_advanced_settings_element_call_base_url_validation_error" = "Invalid URL, please make sure you include the protocol (http/https) and the correct address.";
|
||||
"screen_create_room_room_address_section_footer" = "In order for this room to be visible in the public room directory, you will need a room address.";
|
||||
"screen_create_room_room_address_section_title" = "Room address";
|
||||
"screen_create_room_room_visibility_section_title" = "Room visibility";
|
||||
"screen_create_room_access_section_anyone_option_description" = "Anyone can join this room";
|
||||
"screen_create_room_access_section_anyone_option_title" = "Anyone";
|
||||
"screen_create_room_access_section_header" = "Room Access";
|
||||
@ -449,9 +454,12 @@
|
||||
"screen_change_server_title" = "Select your server";
|
||||
"screen_chat_backup_key_backup_action_disable" = "Turn off backup";
|
||||
"screen_chat_backup_key_backup_action_enable" = "Turn on backup";
|
||||
"screen_chat_backup_key_backup_description" = "Backup ensures that you don't lose your message history. %1$@.";
|
||||
"screen_chat_backup_key_backup_title" = "Backup";
|
||||
"screen_chat_backup_key_backup_description" = "Store your cryptographic identity and message keys securely on the server. This will allow you to view your message history on any new devices. %1$@.";
|
||||
"screen_chat_backup_key_backup_title" = "Key storage";
|
||||
"screen_chat_backup_key_storage_toggle_description" = "Upload keys from this device";
|
||||
"screen_chat_backup_key_storage_toggle_title" = "Allow key storage";
|
||||
"screen_chat_backup_recovery_action_change" = "Change recovery key";
|
||||
"screen_chat_backup_recovery_action_change_description" = "Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices.";
|
||||
"screen_chat_backup_recovery_action_confirm_description" = "Your chat backup is currently out of sync.";
|
||||
"screen_chat_backup_recovery_action_setup" = "Set up recovery";
|
||||
"screen_chat_backup_recovery_action_setup_description" = "Get access to your encrypted messages if you lose all your devices or are signed out of %1$@ everywhere.";
|
||||
@ -473,10 +481,10 @@
|
||||
"screen_create_poll_title" = "Create Poll";
|
||||
"screen_create_room_action_create_room" = "New room";
|
||||
"screen_create_room_error_creating_room" = "An error occurred when creating the room";
|
||||
"screen_create_room_private_option_description" = "Messages in this room are encrypted. Encryption can’t be disabled afterwards.";
|
||||
"screen_create_room_private_option_title" = "Private room (invite only)";
|
||||
"screen_create_room_public_option_description" = "Messages are not encrypted and anyone can read them. You can enable encryption at a later date.";
|
||||
"screen_create_room_public_option_title" = "Public room (anyone)";
|
||||
"screen_create_room_private_option_description" = "Only people invited can access this room. All messages are end-to-end encrypted.";
|
||||
"screen_create_room_private_option_title" = "Private room";
|
||||
"screen_create_room_public_option_description" = "Anyone can find this room.\nYou can change this anytime in room settings.";
|
||||
"screen_create_room_public_option_title" = "Public room";
|
||||
"screen_create_room_topic_label" = "Topic (optional)";
|
||||
"screen_deactivate_account_confirmation_dialog_content" = "Please confirm that you want to deactivate your account. This action cannot be undone.";
|
||||
"screen_deactivate_account_delete_all_messages" = "Delete all my messages";
|
||||
@ -624,7 +632,6 @@
|
||||
"screen_qr_code_login_verify_code_title" = "Your verification code";
|
||||
"screen_recovery_key_change_description" = "Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.";
|
||||
"screen_recovery_key_change_generate_key" = "Generate a new recovery key";
|
||||
"screen_recovery_key_change_generate_key_description" = "Make sure you can store your recovery key somewhere safe";
|
||||
"screen_recovery_key_change_success" = "Recovery key changed";
|
||||
"screen_recovery_key_change_title" = "Change recovery key?";
|
||||
"screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key";
|
||||
@ -638,14 +645,14 @@
|
||||
"screen_recovery_key_copied_to_clipboard" = "Copied recovery key";
|
||||
"screen_recovery_key_generating_key" = "Generating…";
|
||||
"screen_recovery_key_save_action" = "Save recovery key";
|
||||
"screen_recovery_key_save_description" = "Write down your recovery key somewhere safe or save it in a password manager.";
|
||||
"screen_recovery_key_save_description" = "Write down this recovery key somewhere safe, like a password manager, encrypted note, or a physical safe.";
|
||||
"screen_recovery_key_save_key_description" = "Tap to copy recovery key";
|
||||
"screen_recovery_key_save_title" = "Save your recovery key";
|
||||
"screen_recovery_key_save_title" = "Save your recovery key somewhere safe";
|
||||
"screen_recovery_key_setup_confirmation_description" = "You will not be able to access your new recovery key after this step.";
|
||||
"screen_recovery_key_setup_confirmation_title" = "Have you saved your recovery key?";
|
||||
"screen_recovery_key_setup_description" = "Your chat backup is protected by a recovery key. If you need a new recovery key after setup you can recreate by selecting ‘Change recovery key’.";
|
||||
"screen_recovery_key_setup_generate_key" = "Generate your recovery key";
|
||||
"screen_recovery_key_setup_generate_key_description" = "Make sure you can store your recovery key somewhere safe";
|
||||
"screen_recovery_key_setup_generate_key_description" = "Do not share this with anyone!";
|
||||
"screen_recovery_key_setup_success" = "Recovery setup successful";
|
||||
"screen_recovery_key_setup_title" = "Set up recovery";
|
||||
"screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user";
|
||||
@ -1019,6 +1026,7 @@
|
||||
"screen_login_subtitle" = "Matrix is an open network for secure, decentralised communication.";
|
||||
"screen_notification_settings_mentions_section_title" = "Mentions";
|
||||
"screen_qr_code_login_invalid_scan_state_retry_button" = "Try again";
|
||||
"screen_recovery_key_change_generate_key_description" = "Do not share this with anyone!";
|
||||
"screen_recovery_key_confirm_title" = "Enter your recovery key";
|
||||
"screen_report_content_block_user" = "Block user";
|
||||
"screen_reset_encryption_password_placeholder" = "Enter…";
|
||||
|
@ -48,6 +48,7 @@ final class AppSettings {
|
||||
case enableOnlySignedDeviceIsolationMode
|
||||
case identityPinningViolationNotificationsEnabled
|
||||
case knockingEnabled
|
||||
case frequentEmojisEnabled
|
||||
}
|
||||
|
||||
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
|
||||
@ -289,6 +290,9 @@ final class AppSettings {
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var knockingEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.frequentEmojisEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store))
|
||||
var frequentEmojisEnabled
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -22,6 +22,7 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<PinnedEventsTimelineFlowCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<PinnedEventsTimelineFlowCoordinatorAction, Never> {
|
||||
@ -35,13 +36,15 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appMediator: AppMediatorProtocol) {
|
||||
appMediator: AppMediatorProtocol,
|
||||
emojiProvider: EmojiProviderProtocol) {
|
||||
self.navigationStackCoordinator = navigationStackCoordinator
|
||||
self.userSession = userSession
|
||||
self.roomTimelineControllerFactory = roomTimelineControllerFactory
|
||||
self.roomProxy = roomProxy
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appMediator = appMediator
|
||||
self.emojiProvider = emojiProvider
|
||||
}
|
||||
|
||||
func start() {
|
||||
@ -71,7 +74,8 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
|
||||
mediaProvider: userSession.mediaProvider,
|
||||
mediaPlayerProvider: MediaPlayerProvider(),
|
||||
voiceMessageMediaManager: userSession.voiceMessageMediaManager,
|
||||
appMediator: appMediator))
|
||||
appMediator: appMediator,
|
||||
emojiProvider: emojiProvider))
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
|
@ -1340,7 +1340,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
roomTimelineControllerFactory: roomTimelineControllerFactory,
|
||||
roomProxy: roomProxy,
|
||||
userIndicatorController: userIndicatorController,
|
||||
appMediator: appMediator)
|
||||
appMediator: appMediator,
|
||||
emojiProvider: emojiProvider)
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else {
|
||||
|
@ -492,7 +492,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
isChildFlow: false,
|
||||
roomTimelineControllerFactory: roomTimelineControllerFactory,
|
||||
navigationStackCoordinator: detailNavigationStackCoordinator,
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||
ongoingCallRoomIDPublisher: elementCallService.ongoingCallRoomIDPublisher,
|
||||
appMediator: appMediator,
|
||||
appSettings: appSettings,
|
||||
|
@ -312,6 +312,8 @@ internal enum L10n {
|
||||
internal static func commonEmote(_ p1: Any, _ p2: Any) -> String {
|
||||
return L10n.tr("Localizable", "common_emote", String(describing: p1), String(describing: p2))
|
||||
}
|
||||
/// Encryption
|
||||
internal static var commonEncryption: String { return L10n.tr("Localizable", "common_encryption") }
|
||||
/// Encryption enabled
|
||||
internal static var commonEncryptionEnabled: String { return L10n.tr("Localizable", "common_encryption_enabled") }
|
||||
/// Enter your PIN
|
||||
@ -332,6 +334,8 @@ internal enum L10n {
|
||||
internal static var commonFile: String { return L10n.tr("Localizable", "common_file") }
|
||||
/// Forward message
|
||||
internal static var commonForwardMessage: String { return L10n.tr("Localizable", "common_forward_message") }
|
||||
/// Frequently used
|
||||
internal static var commonFrequentlyUsed: String { return L10n.tr("Localizable", "common_frequently_used") }
|
||||
/// GIF
|
||||
internal static var commonGif: String { return L10n.tr("Localizable", "common_gif") }
|
||||
/// Image
|
||||
@ -1005,14 +1009,20 @@ internal enum L10n {
|
||||
internal static var screenChatBackupKeyBackupActionDisable: String { return L10n.tr("Localizable", "screen_chat_backup_key_backup_action_disable") }
|
||||
/// Turn on backup
|
||||
internal static var screenChatBackupKeyBackupActionEnable: String { return L10n.tr("Localizable", "screen_chat_backup_key_backup_action_enable") }
|
||||
/// Backup ensures that you don't lose your message history. %1$@.
|
||||
/// Store your cryptographic identity and message keys securely on the server. This will allow you to view your message history on any new devices. %1$@.
|
||||
internal static func screenChatBackupKeyBackupDescription(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_chat_backup_key_backup_description", String(describing: p1))
|
||||
}
|
||||
/// Backup
|
||||
/// Key storage
|
||||
internal static var screenChatBackupKeyBackupTitle: String { return L10n.tr("Localizable", "screen_chat_backup_key_backup_title") }
|
||||
/// Upload keys from this device
|
||||
internal static var screenChatBackupKeyStorageToggleDescription: String { return L10n.tr("Localizable", "screen_chat_backup_key_storage_toggle_description") }
|
||||
/// Allow key storage
|
||||
internal static var screenChatBackupKeyStorageToggleTitle: String { return L10n.tr("Localizable", "screen_chat_backup_key_storage_toggle_title") }
|
||||
/// Change recovery key
|
||||
internal static var screenChatBackupRecoveryActionChange: String { return L10n.tr("Localizable", "screen_chat_backup_recovery_action_change") }
|
||||
/// Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices.
|
||||
internal static var screenChatBackupRecoveryActionChangeDescription: String { return L10n.tr("Localizable", "screen_chat_backup_recovery_action_change_description") }
|
||||
/// Enter recovery key
|
||||
internal static var screenChatBackupRecoveryActionConfirm: String { return L10n.tr("Localizable", "screen_chat_backup_recovery_action_confirm") }
|
||||
/// Your chat backup is currently out of sync.
|
||||
@ -1079,16 +1089,23 @@ internal enum L10n {
|
||||
internal static var screenCreateRoomAddPeopleTitle: String { return L10n.tr("Localizable", "screen_create_room_add_people_title") }
|
||||
/// An error occurred when creating the room
|
||||
internal static var screenCreateRoomErrorCreatingRoom: String { return L10n.tr("Localizable", "screen_create_room_error_creating_room") }
|
||||
/// Messages in this room are encrypted. Encryption can’t be disabled afterwards.
|
||||
/// Only people invited can access this room. All messages are end-to-end encrypted.
|
||||
internal static var screenCreateRoomPrivateOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_private_option_description") }
|
||||
/// Private room (invite only)
|
||||
/// Private room
|
||||
internal static var screenCreateRoomPrivateOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_private_option_title") }
|
||||
/// Messages are not encrypted and anyone can read them. You can enable encryption at a later date.
|
||||
/// Anyone can find this room.
|
||||
/// You can change this anytime in room settings.
|
||||
internal static var screenCreateRoomPublicOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_public_option_description") }
|
||||
/// Public room (anyone)
|
||||
/// Public room
|
||||
internal static var screenCreateRoomPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_public_option_title") }
|
||||
/// In order for this room to be visible in the public room directory, you will need a room address.
|
||||
internal static var screenCreateRoomRoomAddressSectionFooter: String { return L10n.tr("Localizable", "screen_create_room_room_address_section_footer") }
|
||||
/// Room address
|
||||
internal static var screenCreateRoomRoomAddressSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_address_section_title") }
|
||||
/// Room name
|
||||
internal static var screenCreateRoomRoomNameLabel: String { return L10n.tr("Localizable", "screen_create_room_room_name_label") }
|
||||
/// Room visibility
|
||||
internal static var screenCreateRoomRoomVisibilitySectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_visibility_section_title") }
|
||||
/// Create a room
|
||||
internal static var screenCreateRoomTitle: String { return L10n.tr("Localizable", "screen_create_room_title") }
|
||||
/// Topic (optional)
|
||||
@ -1475,7 +1492,7 @@ internal enum L10n {
|
||||
internal static var screenRecoveryKeyChangeDescription: String { return L10n.tr("Localizable", "screen_recovery_key_change_description") }
|
||||
/// Generate a new recovery key
|
||||
internal static var screenRecoveryKeyChangeGenerateKey: String { return L10n.tr("Localizable", "screen_recovery_key_change_generate_key") }
|
||||
/// Make sure you can store your recovery key somewhere safe
|
||||
/// Do not share this with anyone!
|
||||
internal static var screenRecoveryKeyChangeGenerateKeyDescription: String { return L10n.tr("Localizable", "screen_recovery_key_change_generate_key_description") }
|
||||
/// Recovery key changed
|
||||
internal static var screenRecoveryKeyChangeSuccess: String { return L10n.tr("Localizable", "screen_recovery_key_change_success") }
|
||||
@ -1505,11 +1522,11 @@ internal enum L10n {
|
||||
internal static var screenRecoveryKeyGeneratingKey: String { return L10n.tr("Localizable", "screen_recovery_key_generating_key") }
|
||||
/// Save recovery key
|
||||
internal static var screenRecoveryKeySaveAction: String { return L10n.tr("Localizable", "screen_recovery_key_save_action") }
|
||||
/// Write down your recovery key somewhere safe or save it in a password manager.
|
||||
/// Write down this recovery key somewhere safe, like a password manager, encrypted note, or a physical safe.
|
||||
internal static var screenRecoveryKeySaveDescription: String { return L10n.tr("Localizable", "screen_recovery_key_save_description") }
|
||||
/// Tap to copy recovery key
|
||||
internal static var screenRecoveryKeySaveKeyDescription: String { return L10n.tr("Localizable", "screen_recovery_key_save_key_description") }
|
||||
/// Save your recovery key
|
||||
/// Save your recovery key somewhere safe
|
||||
internal static var screenRecoveryKeySaveTitle: String { return L10n.tr("Localizable", "screen_recovery_key_save_title") }
|
||||
/// You will not be able to access your new recovery key after this step.
|
||||
internal static var screenRecoveryKeySetupConfirmationDescription: String { return L10n.tr("Localizable", "screen_recovery_key_setup_confirmation_description") }
|
||||
@ -1519,7 +1536,7 @@ internal enum L10n {
|
||||
internal static var screenRecoveryKeySetupDescription: String { return L10n.tr("Localizable", "screen_recovery_key_setup_description") }
|
||||
/// Generate your recovery key
|
||||
internal static var screenRecoveryKeySetupGenerateKey: String { return L10n.tr("Localizable", "screen_recovery_key_setup_generate_key") }
|
||||
/// Make sure you can store your recovery key somewhere safe
|
||||
/// Do not share this with anyone!
|
||||
internal static var screenRecoveryKeySetupGenerateKeyDescription: String { return L10n.tr("Localizable", "screen_recovery_key_setup_generate_key_description") }
|
||||
/// Recovery setup successful
|
||||
internal static var screenRecoveryKeySetupSuccess: String { return L10n.tr("Localizable", "screen_recovery_key_setup_success") }
|
||||
|
@ -44,6 +44,8 @@ struct EmojiPickerEmojiCategoryViewData: Identifiable {
|
||||
return L10n.emojiPickerCategorySymbols
|
||||
case "flags":
|
||||
return L10n.emojiPickerCategoryFlags
|
||||
case EmojiCategory.frequentlyUsedCategoryIdentifier:
|
||||
return L10n.commonFrequentlyUsed
|
||||
default:
|
||||
MXLog.failure("Missing translation for emoji category with id \(id)")
|
||||
return ""
|
||||
|
@ -36,6 +36,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
||||
state.categories = convert(emojiCategories: categories)
|
||||
}
|
||||
case let .emojiTapped(emoji: emoji):
|
||||
emojiProvider.markEmojiAsFrequentlyUsed(emoji.value)
|
||||
actionsSubject.send(.emojiSelected(emoji: emoji.value))
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
|
@ -81,7 +81,7 @@ struct EmojiPickerScreen: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = EmojiPickerScreenViewModel(emojiProvider: EmojiProvider())
|
||||
static let viewModel = EmojiPickerScreenViewModel(emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
EmojiPickerScreen(context: viewModel.context, selectedEmojis: ["😀", "😄"])
|
||||
@ -91,7 +91,7 @@ struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
|
||||
struct EmojiPickerScreenSheet_Previews: PreviewProvider {
|
||||
static let viewModel = EmojiPickerScreenViewModel(emojiProvider: EmojiProvider())
|
||||
static let viewModel = EmojiPickerScreenViewModel(emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
Text("Timeline view")
|
||||
|
@ -15,6 +15,7 @@ struct PinnedEventsTimelineScreenCoordinatorParameters {
|
||||
let mediaPlayerProvider: MediaPlayerProviderProtocol
|
||||
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||
let appMediator: AppMediatorProtocol
|
||||
let emojiProvider: EmojiProviderProtocol
|
||||
}
|
||||
|
||||
enum PinnedEventsTimelineScreenCoordinatorAction {
|
||||
@ -49,7 +50,8 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: parameters.appMediator,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: parameters.emojiProvider)
|
||||
}
|
||||
|
||||
func start() {
|
||||
|
@ -37,7 +37,8 @@ struct PinnedEventsTimelineScreen: View {
|
||||
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
||||
isDM: timelineContext.viewState.isEncryptedOneToOneRoom,
|
||||
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
||||
isPinnedEventsTimeline: timelineContext.viewState.isPinnedEventsTimeline)
|
||||
isPinnedEventsTimeline: timelineContext.viewState.isPinnedEventsTimeline,
|
||||
emojiProvider: timelineContext.viewState.emojiProvider)
|
||||
.makeActions()
|
||||
if let actions {
|
||||
TimelineItemMenu(item: info.item, actions: actions)
|
||||
@ -96,7 +97,8 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
|
@ -81,7 +81,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: parameters.appMediator,
|
||||
appSettings: parameters.appSettings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: parameters.emojiProvider)
|
||||
|
||||
wysiwygViewModel = WysiwygComposerViewModel(minHeight: ComposerConstant.minHeight,
|
||||
maxCompressedHeight: ComposerConstant.maxHeight,
|
||||
|
@ -76,7 +76,8 @@ struct RoomScreen: View {
|
||||
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
||||
isDM: timelineContext.viewState.isEncryptedOneToOneRoom,
|
||||
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
||||
isPinnedEventsTimeline: timelineContext.viewState.isPinnedEventsTimeline)
|
||||
isPinnedEventsTimeline: timelineContext.viewState.isPinnedEventsTimeline,
|
||||
emojiProvider: timelineContext.viewState.emojiProvider)
|
||||
.makeActions()
|
||||
if let actions {
|
||||
TimelineItemMenu(item: info.item, actions: actions)
|
||||
@ -229,7 +230,8 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -51,6 +51,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
||||
var elementCallBaseURLOverride: URL? { get set }
|
||||
var identityPinningViolationNotificationsEnabled: Bool { get set }
|
||||
var knockingEnabled: Bool { get set }
|
||||
var frequentEmojisEnabled: Bool { get set }
|
||||
}
|
||||
|
||||
extension AppSettings: DeveloperOptionsProtocol { }
|
||||
|
@ -53,6 +53,10 @@ struct DeveloperOptionsScreen: View {
|
||||
Toggle(isOn: $context.identityPinningViolationNotificationsEnabled) {
|
||||
Text("Identity pinning violation notifications")
|
||||
}
|
||||
|
||||
Toggle(isOn: $context.frequentEmojisEnabled) {
|
||||
Text("Show frequently used emojis")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Join rules") {
|
||||
|
@ -111,6 +111,8 @@ struct TimelineViewState: BindableState {
|
||||
|
||||
/// A closure providing the associated audio player state for an item in the timeline.
|
||||
var audioPlayerStateProvider: (@MainActor (_ itemId: TimelineItemIdentifier) -> AudioPlayerState?)?
|
||||
|
||||
var emojiProvider: EmojiProviderProtocol
|
||||
}
|
||||
|
||||
struct TimelineViewStateBindings {
|
||||
|
@ -28,6 +28,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analyticsService: AnalyticsService
|
||||
private let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
private let timelineInteractionHandler: TimelineInteractionHandler
|
||||
|
||||
@ -50,7 +51,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appMediator: AppMediatorProtocol,
|
||||
appSettings: AppSettings,
|
||||
analyticsService: AnalyticsService) {
|
||||
analyticsService: AnalyticsService,
|
||||
emojiProvider: EmojiProviderProtocol) {
|
||||
self.timelineController = timelineController
|
||||
self.mediaPlayerProvider = mediaPlayerProvider
|
||||
self.roomProxy = roomProxy
|
||||
@ -58,6 +60,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
self.analyticsService = analyticsService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appMediator = appMediator
|
||||
self.emojiProvider = emojiProvider
|
||||
|
||||
let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider)
|
||||
|
||||
@ -79,7 +82,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
ownUserID: roomProxy.ownUserID,
|
||||
isViewSourceEnabled: appSettings.viewSourceEnabled,
|
||||
hideTimelineMedia: appSettings.hideTimelineMedia,
|
||||
bindings: .init(reactionsCollapsed: [:])),
|
||||
bindings: .init(reactionsCollapsed: [:]),
|
||||
emojiProvider: emojiProvider),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
if focussedEventID != nil {
|
||||
@ -132,6 +136,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
case .itemSendInfoTapped(let itemID):
|
||||
handleItemSendInfoTapped(itemID: itemID)
|
||||
case .toggleReaction(let emoji, let itemID):
|
||||
emojiProvider.markEmojiAsFrequentlyUsed(emoji)
|
||||
|
||||
guard case let .event(_, eventOrTransactionID) = itemID else {
|
||||
fatalError()
|
||||
}
|
||||
@ -861,7 +867,8 @@ extension TimelineViewModel {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static let pinnedEventsTimelineMock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
|
||||
focussedEventID: nil,
|
||||
@ -872,7 +879,8 @@ extension TimelineViewModel {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
@ -308,7 +308,8 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
|
||||
guard var item = RoomTimelineItemFixtures.singleMessageChunk.first as? TextRoomTimelineItem,
|
||||
let actions = TimelineItemMenuActions(isReactable: true,
|
||||
actions: [.copy, .edit, .reply(isThread: false), .pin, .redact],
|
||||
debugActions: [.viewSource]) else {
|
||||
debugActions: [.viewSource],
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -8,26 +8,38 @@
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct TimelineItemMenuActions {
|
||||
let reactions: [TimelineItemMenuReaction]
|
||||
let actions: [TimelineItemMenuAction]
|
||||
let debugActions: [TimelineItemMenuAction]
|
||||
|
||||
init?(isReactable: Bool, actions: [TimelineItemMenuAction], debugActions: [TimelineItemMenuAction]) {
|
||||
init?(isReactable: Bool,
|
||||
actions: [TimelineItemMenuAction],
|
||||
debugActions: [TimelineItemMenuAction],
|
||||
emojiProvider: EmojiProviderProtocol) {
|
||||
if !isReactable, actions.isEmpty, debugActions.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.actions = actions
|
||||
self.debugActions = debugActions
|
||||
|
||||
// Only process 5 of the most frequently used emojis instead of all of them
|
||||
var frequentlyUsed = emojiProvider.frequentlyUsedSystemEmojis().prefix(5).map { TimelineItemMenuReaction(key: $0, symbol: .heart) }
|
||||
|
||||
frequentlyUsed += [
|
||||
.init(key: "👍️", symbol: .handThumbsup),
|
||||
.init(key: "👎️", symbol: .handThumbsdown),
|
||||
.init(key: "🔥", symbol: .flame),
|
||||
.init(key: "❤️", symbol: .heart),
|
||||
.init(key: "👏", symbol: .handsClap)
|
||||
]
|
||||
|
||||
frequentlyUsed = Array(frequentlyUsed.prefix(5))
|
||||
|
||||
reactions = if isReactable {
|
||||
[
|
||||
.init(key: "👍️", symbol: .handThumbsup),
|
||||
.init(key: "👎️", symbol: .handThumbsdown),
|
||||
.init(key: "🔥", symbol: .flame),
|
||||
.init(key: "❤️", symbol: .heart),
|
||||
.init(key: "👏", symbol: .handsClap)
|
||||
]
|
||||
frequentlyUsed
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
struct TimelineItemMenuActionProvider {
|
||||
let timelineItem: RoomTimelineItemProtocol
|
||||
let canCurrentUserRedactSelf: Bool
|
||||
@ -16,6 +17,7 @@ struct TimelineItemMenuActionProvider {
|
||||
let isDM: Bool
|
||||
let isViewSourceEnabled: Bool
|
||||
let isPinnedEventsTimeline: Bool
|
||||
let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func makeActions() -> TimelineItemMenuActions? {
|
||||
@ -42,7 +44,10 @@ struct TimelineItemMenuActionProvider {
|
||||
break
|
||||
}
|
||||
|
||||
return .init(isReactable: false, actions: [.copyPermalink], debugActions: debugActions)
|
||||
return .init(isReactable: false,
|
||||
actions: [.copyPermalink],
|
||||
debugActions: debugActions,
|
||||
emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
var actions: [TimelineItemMenuAction] = []
|
||||
@ -100,7 +105,10 @@ struct TimelineItemMenuActionProvider {
|
||||
actions = actions.filter(\.canAppearInPinnedEventsTimeline)
|
||||
}
|
||||
|
||||
return .init(isReactable: isPinnedEventsTimeline ? false : item.isReactable, actions: actions, debugActions: debugActions)
|
||||
return .init(isReactable: isPinnedEventsTimeline ? false : item.isReactable,
|
||||
actions: actions,
|
||||
debugActions: debugActions,
|
||||
emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
private func canRedactItem(_ item: EventBasedTimelineItemProtocol) -> Bool {
|
||||
|
@ -51,7 +51,8 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
return mock
|
||||
}()
|
||||
|
||||
|
@ -148,7 +148,8 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
pinnedEventIDs: context.viewState.pinnedEventIDs,
|
||||
isDM: context.viewState.isEncryptedOneToOneRoom,
|
||||
isViewSourceEnabled: context.viewState.isViewSourceEnabled,
|
||||
isPinnedEventsTimeline: context.viewState.isPinnedEventsTimeline)
|
||||
isPinnedEventsTimeline: context.viewState.isPinnedEventsTimeline,
|
||||
emojiProvider: context.viewState.emojiProvider)
|
||||
TimelineItemMacContextMenu(item: timelineItem, actionProvider: provider) { action in
|
||||
context.send(viewAction: .handleTimelineItemMenuAction(itemID: timelineItem.id, action: action))
|
||||
}
|
||||
@ -364,7 +365,8 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
|
@ -89,7 +89,8 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")]
|
||||
static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"),
|
||||
|
@ -96,7 +96,8 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -89,7 +89,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -1,13 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiCategory: Equatable, Identifiable {
|
||||
let id: String
|
||||
let emojis: [EmojiItem]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiItem: Equatable, Identifiable {
|
||||
var id: String {
|
||||
label
|
||||
}
|
||||
|
||||
let label: String
|
||||
let unicode: String
|
||||
let keywords: [String]
|
||||
let shortcodes: [String]
|
||||
}
|
@ -7,31 +7,38 @@
|
||||
|
||||
import Emojibase
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
protocol EmojiProviderProtocol {
|
||||
func categories(searchString: String?) async -> [EmojiCategory]
|
||||
}
|
||||
|
||||
private enum EmojiProviderState {
|
||||
case notLoaded
|
||||
case inProgress(Task<[EmojiCategory], Never>)
|
||||
case loaded([EmojiCategory])
|
||||
}
|
||||
import OrderedCollections
|
||||
|
||||
class EmojiProvider: EmojiProviderProtocol {
|
||||
private let loader: EmojiLoaderProtocol
|
||||
private var state: EmojiProviderState = .notLoaded
|
||||
private let appSettings: AppSettings
|
||||
|
||||
init(loader: EmojiLoaderProtocol = EmojibaseDatasource()) {
|
||||
private(set) var state: EmojiProviderState = .notLoaded
|
||||
|
||||
init(loader: EmojiLoaderProtocol = EmojibaseDatasource(), appSettings: AppSettings) {
|
||||
self.loader = loader
|
||||
self.appSettings = appSettings
|
||||
|
||||
Task {
|
||||
await loadIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
func categories(searchString: String? = nil) async -> [EmojiCategory] {
|
||||
let emojiCategories = await loadIfNeeded()
|
||||
var emojiCategories = await loadIfNeeded()
|
||||
|
||||
let allEmojis = emojiCategories.reduce([]) { partialResult, category in
|
||||
partialResult + category.emojis
|
||||
}
|
||||
|
||||
// Map frequently used system unicode emojis to our emoji provider ones
|
||||
let frequentlyUsedEmojis = frequentlyUsedSystemEmojis().prefix(20)
|
||||
let emojis = allEmojis.filter { frequentlyUsedEmojis.contains($0.unicode) }
|
||||
|
||||
if !emojis.isEmpty {
|
||||
emojiCategories.insert(.init(id: EmojiCategory.frequentlyUsedCategoryIdentifier, emojis: emojis), at: 0)
|
||||
}
|
||||
|
||||
if let searchString, searchString.isEmpty == false {
|
||||
return search(searchString: searchString, emojiCategories: emojiCategories)
|
||||
} else {
|
||||
@ -39,6 +46,40 @@ class EmojiProvider: EmojiProviderProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func frequentlyUsedSystemEmojis() -> [String] {
|
||||
guard appSettings.frequentEmojisEnabled, !ProcessInfo.processInfo.isiOSAppOnMac else {
|
||||
return []
|
||||
}
|
||||
|
||||
guard let preferences = UserDefaults(suiteName: "com.apple.EmojiPreferences"),
|
||||
let defaults = preferences.dictionary(forKey: "EMFDefaultsKey"),
|
||||
let recents = defaults["EMFRecentsKey"] as? [String]
|
||||
else {
|
||||
return []
|
||||
}
|
||||
|
||||
return recents
|
||||
}
|
||||
|
||||
func markEmojiAsFrequentlyUsed(_ emoji: String) {
|
||||
guard appSettings.frequentEmojisEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let preferences = UserDefaults(suiteName: "com.apple.EmojiPreferences"),
|
||||
let defaults = preferences.dictionary(forKey: "EMFDefaultsKey"),
|
||||
let recents = defaults["EMFRecentsKey"] as? [String] else {
|
||||
return
|
||||
}
|
||||
|
||||
var uniqueOrderedRecents = OrderedSet(recents)
|
||||
uniqueOrderedRecents.insert(emoji, at: 0)
|
||||
|
||||
preferences.setValue(["EMFRecentsKey": Array(uniqueOrderedRecents)], forKey: "EMFDefaultsKey")
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func search(searchString: String, emojiCategories: [EmojiCategory]) -> [EmojiCategory] {
|
||||
emojiCategories.compactMap { category in
|
||||
let emojis = category.emojis.filter { emoji in
|
||||
|
42
ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift
Normal file
42
ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiItem: Equatable, Identifiable {
|
||||
var id: String {
|
||||
label
|
||||
}
|
||||
|
||||
let label: String
|
||||
let unicode: String
|
||||
let keywords: [String]
|
||||
let shortcodes: [String]
|
||||
}
|
||||
|
||||
struct EmojiCategory: Equatable, Identifiable {
|
||||
static let frequentlyUsedCategoryIdentifier = "io.element.elementx.frequently_used"
|
||||
|
||||
let id: String
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
enum EmojiProviderState {
|
||||
case notLoaded
|
||||
case inProgress(Task<[EmojiCategory], Never>)
|
||||
case loaded([EmojiCategory])
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol EmojiProviderProtocol {
|
||||
var state: EmojiProviderState { get }
|
||||
|
||||
func categories(searchString: String?) async -> [EmojiCategory]
|
||||
|
||||
func frequentlyUsedSystemEmojis() -> [String]
|
||||
func markEmojiAsFrequentlyUsed(_ emoji: String)
|
||||
}
|
@ -239,7 +239,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -258,7 +258,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -277,7 +277,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -296,7 +296,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -318,7 +318,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -340,7 +340,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -362,7 +362,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -385,7 +385,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -407,7 +407,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -428,7 +428,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -463,7 +463,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -485,7 +485,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
@ -507,7 +507,7 @@ class MockScreen: Identifiable {
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
|
@ -18,7 +18,7 @@ final class EmojiProviderTests: XCTestCase {
|
||||
let emojiLoaderMock = EmojiLoaderMock()
|
||||
emojiLoaderMock.categories = [category]
|
||||
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock, appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
let categories = await emojiProvider.categories()
|
||||
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
||||
@ -31,7 +31,7 @@ final class EmojiProviderTests: XCTestCase {
|
||||
let emojiLoaderMock = EmojiLoaderMock()
|
||||
emojiLoaderMock.categories = [category]
|
||||
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock, appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
let categories = await emojiProvider.categories(searchString: "")
|
||||
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
||||
@ -48,7 +48,7 @@ final class EmojiProviderTests: XCTestCase {
|
||||
let emojiLoaderMock = EmojiLoaderMock()
|
||||
emojiLoaderMock.categories = categoriesForFirstLoad
|
||||
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock, appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
_ = await emojiProvider.categories()
|
||||
emojiLoaderMock.categories = categoriesForSecondLoad
|
||||
@ -78,7 +78,7 @@ final class EmojiProviderTests: XCTestCase {
|
||||
let emojiLoaderMock = EmojiLoaderMock()
|
||||
emojiLoaderMock.categories = categories
|
||||
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||
let emojiProvider = EmojiProvider(loader: emojiLoaderMock, appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
_ = await emojiProvider.categories()
|
||||
let result = await emojiProvider.categories(searchString: searchString)
|
||||
|
@ -25,7 +25,8 @@ class PillContextTests: XCTestCase {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertFalse(context.viewState.isOwnMention)
|
||||
@ -53,7 +54,8 @@ class PillContextTests: XCTestCase {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertTrue(context.viewState.isOwnMention)
|
||||
@ -74,7 +76,8 @@ class PillContextTests: XCTestCase {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertTrue(context.viewState.isOwnMention)
|
||||
|
@ -298,7 +298,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
isChildFlow: asChildFlow,
|
||||
roomTimelineControllerFactory: timelineControllerFactory,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
emojiProvider: EmojiProvider(),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
ongoingCallRoomIDPublisher: .init(.init(nil)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
|
@ -310,7 +310,8 @@ class TimelineViewModelTests: XCTestCase {
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
return (viewModel, roomProxy, timelineProxy, timelineController)
|
||||
}
|
||||
|
||||
@ -334,7 +335,8 @@ class TimelineViewModelTests: XCTestCase {
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||
value.bindings.readReceiptsSummaryInfo?.orderedReceipts == receipts
|
||||
@ -360,7 +362,8 @@ class TimelineViewModelTests: XCTestCase {
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||
value.pinnedEventIDs == ["test1"]
|
||||
@ -388,7 +391,8 @@ class TimelineViewModelTests: XCTestCase {
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||
value.canCurrentUserPin
|
||||
@ -417,7 +421,8 @@ class TimelineViewModelTests: XCTestCase {
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user